forked from AkkomaGang/admin-fe
Merge branch 'feature/include-usernames-in-reports-modlogs' into 'develop'
Include usernames in all relevant reports actions in Moderation Log Closes #145 See merge request pleroma/admin-fe!185
This commit is contained in:
commit
f04ded8fa4
9 changed files with 157 additions and 82 deletions
|
@ -14,12 +14,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Add Unconfimed filter for Users table
|
- Add Unconfimed filter for Users table
|
||||||
- Filter users by actor type: Person, Bot or Application
|
- Filter users by actor type: Person, Bot or Application
|
||||||
- Add ability to configure Media Preview Proxy, User Backup and Websocket based federation settings
|
- Add ability to configure Media Preview Proxy, User Backup and Websocket based federation settings
|
||||||
|
- Mobile and Tablet UI for Single Report show page
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Hide Tag actions on Users tab if MRF TagPolicy is disabled. Add ability to enable TagPolicy from Moderation menu
|
- Hide Tag actions on Users tab if MRF TagPolicy is disabled. Add ability to enable TagPolicy from Moderation menu
|
||||||
- Move `:restrict_unauthenticated` settings from Authentication tab to Instance tab
|
- Move `:restrict_unauthenticated` settings from Authentication tab to Instance tab
|
||||||
- Replace regular inputs with textareas for setting welcome messages in the Settings section
|
- Replace regular inputs with textareas for setting welcome messages in the Settings section
|
||||||
|
- Update rendering Moderation Log Messages so that all usernames are links to the pages of the corresponding users in Admin-FE
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ const reports = {
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
fetchedReports: [],
|
fetchedReports: [],
|
||||||
loading: true,
|
loading: true,
|
||||||
|
loadingSingleReport: true,
|
||||||
openReportsCount: 0,
|
openReportsCount: 0,
|
||||||
pageSize: 50,
|
pageSize: 50,
|
||||||
singleReport: {},
|
singleReport: {},
|
||||||
|
@ -42,6 +43,9 @@ const reports = {
|
||||||
},
|
},
|
||||||
SET_SINGLE_REPORT: (state, report) => {
|
SET_SINGLE_REPORT: (state, report) => {
|
||||||
state.singleReport = report
|
state.singleReport = report
|
||||||
|
},
|
||||||
|
SET_SINGLE_REPORT_LOADING: (state, status) => {
|
||||||
|
state.loadingSingleReport = status
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
@ -162,11 +166,11 @@ const reports = {
|
||||||
commit('SET_LOADING', false)
|
commit('SET_LOADING', false)
|
||||||
},
|
},
|
||||||
async FetchSingleReport({ commit, getters }, id) {
|
async FetchSingleReport({ commit, getters }, id) {
|
||||||
commit('SET_LOADING', true)
|
commit('SET_SINGLE_REPORT_LOADING', true)
|
||||||
const { data } = await fetchSingleReport(id, getters.authHost, getters.token)
|
const { data } = await fetchSingleReport(id, getters.authHost, getters.token)
|
||||||
|
|
||||||
commit('SET_SINGLE_REPORT', data)
|
commit('SET_SINGLE_REPORT', data)
|
||||||
commit('SET_LOADING', false)
|
commit('SET_SINGLE_REPORT_LOADING', false)
|
||||||
},
|
},
|
||||||
async FetchOpenReportsCount({ commit, getters, state }) {
|
async FetchOpenReportsCount({ commit, getters, state }) {
|
||||||
commit('SET_LOADING', true)
|
commit('SET_LOADING', true)
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
<template>
|
|
||||||
<span>
|
|
||||||
<router-link
|
|
||||||
v-if="propertyExists(actor, 'id')"
|
|
||||||
:to="{ name: 'UsersShow', params: { id: actor.id }}"
|
|
||||||
class="router-link">
|
|
||||||
<span v-if="propertyExists(actor, 'nickname')" style="font-weight: 600">
|
|
||||||
@{{ actor.nickname }}
|
|
||||||
</span>
|
|
||||||
</router-link>
|
|
||||||
<span v-if="subject.type === 'report' && propertyExists(subject, 'id')">
|
|
||||||
{{ logEntryMessageWithoutId[0] }}
|
|
||||||
<router-link
|
|
||||||
:to="{ name: 'ReportsShow', params: { id: subject.id }}"
|
|
||||||
class="router-link">
|
|
||||||
<span style="font-weight: 600">#{{ subject.id }}</span>
|
|
||||||
</router-link>
|
|
||||||
{{ logEntryMessageWithoutId[1] }}
|
|
||||||
</span>
|
|
||||||
<span v-else>{{ logEntryMessage }}</span>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'LogEntryMessage',
|
|
||||||
props: {
|
|
||||||
actor: {
|
|
||||||
type: Object,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
message: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
subject: {
|
|
||||||
type: [Object, Array],
|
|
||||||
required: false,
|
|
||||||
default: function() {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
logEntryMessage() {
|
|
||||||
return this.actor.nickname ? this.message.split(this.actor.nickname)[1] : this.message
|
|
||||||
},
|
|
||||||
logEntryMessageWithoutId() {
|
|
||||||
return this.logEntryMessage.split(`#${this.subject.id}`)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
propertyExists(account, property) {
|
|
||||||
return account[property]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style rel='stylesheet/scss' lang='scss'>
|
|
||||||
.router-link {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
20
src/views/moderation_log/ReportLink.vue
Normal file
20
src/views/moderation_log/ReportLink.vue
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<template>
|
||||||
|
<router-link
|
||||||
|
:to="{ name: 'ReportsShow', params: { id }}"
|
||||||
|
class="router-link">
|
||||||
|
<span style="font-weight: 600">#{{ id }}</span>
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'UserLink',
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
20
src/views/moderation_log/UserLink.vue
Normal file
20
src/views/moderation_log/UserLink.vue
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<template>
|
||||||
|
<router-link
|
||||||
|
:to="{ name: 'UsersShow', params: { id: actor }}"
|
||||||
|
class="router-link">
|
||||||
|
<span style="font-weight: 600">@{{ actor }}</span>
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'UserLink',
|
||||||
|
props: {
|
||||||
|
actor: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -43,8 +43,7 @@
|
||||||
v-for="(logEntry, index) in log"
|
v-for="(logEntry, index) in log"
|
||||||
:key="index"
|
:key="index"
|
||||||
:timestamp="normalizeTimestamp(logEntry.time)">
|
:timestamp="normalizeTimestamp(logEntry.time)">
|
||||||
<log-entry-message v-if="propertyExists(logEntry.data.actor, 'nickname')" :actor="logEntry.data.actor" :message="logEntry.message" :subject="logEntry.data.subject"/>
|
<component :is="processedMessage(logEntry)"/>
|
||||||
<span v-else>{{ logEntry.message }}</span>
|
|
||||||
</el-timeline-item>
|
</el-timeline-item>
|
||||||
</el-timeline>
|
</el-timeline>
|
||||||
<div class="pagination">
|
<div class="pagination">
|
||||||
|
@ -65,10 +64,14 @@ import moment from 'moment'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import debounce from 'lodash.debounce'
|
import debounce from 'lodash.debounce'
|
||||||
import RebootButton from '@/components/RebootButton'
|
import RebootButton from '@/components/RebootButton'
|
||||||
import LogEntryMessage from './LogEntryMessage'
|
import ReportLink from './ReportLink'
|
||||||
|
import UserLink from './UserLink'
|
||||||
|
import Vue from 'vue'
|
||||||
|
Vue.component('user-link', UserLink)
|
||||||
|
Vue.component('report-link', ReportLink)
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { RebootButton, LogEntryMessage },
|
components: { RebootButton },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
dateRange: '',
|
dateRange: '',
|
||||||
|
@ -130,6 +133,24 @@ export default {
|
||||||
normalizeTimestamp(timestamp) {
|
normalizeTimestamp(timestamp) {
|
||||||
return moment(timestamp * 1000).format('YYYY-MM-DD HH:mm')
|
return moment(timestamp * 1000).format('YYYY-MM-DD HH:mm')
|
||||||
},
|
},
|
||||||
|
processedMessage(logEntry) {
|
||||||
|
const html = [...logEntry.message.matchAll(/\@(?<nickname>([\w-]+))/g)].map(res => res.groups.nickname)
|
||||||
|
.reduce((acc, nickname) => {
|
||||||
|
return acc.replace(`@${nickname}`, `<user-link actor="${nickname}"/>`)
|
||||||
|
}, logEntry.message)
|
||||||
|
if (this.propertyExists(logEntry.data, 'subject') && logEntry.data.subject.type === 'report') {
|
||||||
|
const updatedHtml = [...html.matchAll(/\#(?<reportId>([\w]+))/g)].map(res => res.groups.reportId)
|
||||||
|
.reduce((acc, id) => {
|
||||||
|
return acc.replace(`#${id}`, `<report-link id="${id}"/>`)
|
||||||
|
}, html)
|
||||||
|
return {
|
||||||
|
template: '<div>' + updatedHtml + '</div>'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
template: '<div>' + html + '</div>'
|
||||||
|
}
|
||||||
|
},
|
||||||
propertyExists(account, property) {
|
propertyExists(account, property) {
|
||||||
return account[property]
|
return account[property]
|
||||||
}
|
}
|
||||||
|
@ -173,6 +194,9 @@ h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 145px;
|
width: 145px;
|
||||||
}
|
}
|
||||||
|
.router-link {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
.search-container {
|
.search-container {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<el-dropdown :hide-on-click="false" trigger="click">
|
<el-dropdown :hide-on-click="false" trigger="click">
|
||||||
<el-button :disabled="!account.id" :size="renderedFrom === 'showPage' ? 'medium' : 'small'" plain icon="el-icon-files">
|
<el-button :disabled="!account.id" :size="renderedFrom === 'showPage' && !isMobile ? 'medium' : 'small'" plain icon="el-icon-files">
|
||||||
{{ $t('reports.moderateUser') }}
|
{{ $t('reports.moderateUser') }}
|
||||||
<i class="el-icon-arrow-down el-icon--right"/>
|
<i class="el-icon-arrow-down el-icon--right"/>
|
||||||
</el-button>
|
</el-button>
|
||||||
|
@ -87,6 +87,9 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
isMobile() {
|
||||||
|
return this.$store.state.app.device === 'mobile'
|
||||||
|
},
|
||||||
tagPolicyEnabled() {
|
tagPolicyEnabled() {
|
||||||
return this.$store.state.users.mrfPolicies.includes('Pleroma.Web.ActivityPub.MRF.TagPolicy')
|
return this.$store.state.users.mrfPolicies.includes('Pleroma.Web.ActivityPub.MRF.TagPolicy')
|
||||||
},
|
},
|
||||||
|
|
|
@ -179,4 +179,15 @@ export default {
|
||||||
.router-link {
|
.router-link {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
@media only screen and (max-width:480px) {
|
||||||
|
.divider {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
.el-card__body {
|
||||||
|
padding: 13px;
|
||||||
|
}
|
||||||
|
.report-account {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -4,7 +4,11 @@
|
||||||
<div class="report-page-header">
|
<div class="report-page-header">
|
||||||
<div v-if="propertyExists(report.account, 'nickname')" class="avatar-name-container">
|
<div v-if="propertyExists(report.account, 'nickname')" class="avatar-name-container">
|
||||||
<h1 >{{ $t('reports.reportOn') }}</h1>
|
<h1 >{{ $t('reports.reportOn') }}</h1>
|
||||||
<el-avatar v-if="propertyExists(report.account, 'avatar')" :src="report.account.avatar" size="large" class="report-page-avatar"/>
|
<el-avatar
|
||||||
|
v-if="propertyExists(report.account, 'avatar')"
|
||||||
|
:src="report.account.avatar"
|
||||||
|
:size="isMobile ? 'small' : 'large'"
|
||||||
|
class="report-page-avatar"/>
|
||||||
<h1>{{ report.account.nickname }}</h1>
|
<h1>{{ report.account.nickname }}</h1>
|
||||||
<a v-if="propertyExists(report.account, 'url')" :href="report.account.url" target="_blank">
|
<a v-if="propertyExists(report.account, 'url')" :href="report.account.url" target="_blank">
|
||||||
<i :title="$t('userProfile.openAccountInInstance')" class="el-icon-top-right"/>
|
<i :title="$t('userProfile.openAccountInInstance')" class="el-icon-top-right"/>
|
||||||
|
@ -12,10 +16,16 @@
|
||||||
</div>
|
</div>
|
||||||
<h1 v-else>{{ $t('reports.report') }}</h1>
|
<h1 v-else>{{ $t('reports.report') }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="report-actions-container">
|
||||||
<el-tag :type="getStateType(report.state)" class="report-tag">{{ capitalizeFirstLetter(report.state) }}</el-tag>
|
<el-tag :type="getStateType(report.state)" class="report-tag">{{ capitalizeFirstLetter(report.state) }}</el-tag>
|
||||||
<el-dropdown trigger="click">
|
<el-dropdown trigger="click">
|
||||||
<el-button plain icon="el-icon-edit" class="report-actions-button">{{ $t('reports.changeState') }}<i class="el-icon-arrow-down el-icon--right"/></el-button>
|
<el-button
|
||||||
|
:size="isMobile ? 'small' : 'medium'"
|
||||||
|
plain
|
||||||
|
icon="el-icon-edit"
|
||||||
|
class="report-actions-button">
|
||||||
|
{{ $t('reports.changeState') }}<i class="el-icon-arrow-down el-icon--right"/>
|
||||||
|
</el-button>
|
||||||
<el-dropdown-menu slot="dropdown">
|
<el-dropdown-menu slot="dropdown">
|
||||||
<el-dropdown-item v-if="report.state !== 'resolved'" @click.native="changeReportState('resolved', report.id)">{{ $t('reports.resolve') }}</el-dropdown-item>
|
<el-dropdown-item v-if="report.state !== 'resolved'" @click.native="changeReportState('resolved', report.id)">{{ $t('reports.resolve') }}</el-dropdown-item>
|
||||||
<el-dropdown-item v-if="report.state !== 'open'" @click.native="changeReportState('open', report.id)">{{ $t('reports.reopen') }}</el-dropdown-item>
|
<el-dropdown-item v-if="report.state !== 'open'" @click.native="changeReportState('open', report.id)">{{ $t('reports.reopen') }}</el-dropdown-item>
|
||||||
|
@ -31,10 +41,12 @@
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<h4 v-if="propertyExists(report.account, 'id')" class="id">{{ $t('reports.id') }}: {{ report.id }}</h4>
|
<h4 v-if="propertyExists(report.account, 'id')" class="id">{{ $t('reports.id') }}: {{ report.id }}</h4>
|
||||||
|
<div class="report-card-container">
|
||||||
<el-card class="report">
|
<el-card class="report">
|
||||||
<report-content :report="report"/>
|
<report-content :report="report"/>
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -46,8 +58,11 @@ export default {
|
||||||
name: 'ReportsShow',
|
name: 'ReportsShow',
|
||||||
components: { ModerateUserDropdown, RebootButton, ReportContent },
|
components: { ModerateUserDropdown, RebootButton, ReportContent },
|
||||||
computed: {
|
computed: {
|
||||||
|
isMobile() {
|
||||||
|
return this.$store.state.app.device === 'mobile'
|
||||||
|
},
|
||||||
loading() {
|
loading() {
|
||||||
return this.$store.state.reports.loading
|
return this.$store.state.reports.loadingSingleReport
|
||||||
},
|
},
|
||||||
report() {
|
report() {
|
||||||
return this.$store.state.reports.singleReport
|
return this.$store.state.reports.singleReport
|
||||||
|
@ -94,11 +109,19 @@ export default {
|
||||||
margin: 0 15px 22px 15px;
|
margin: 0 15px 22px 15px;
|
||||||
}
|
}
|
||||||
.report {
|
.report {
|
||||||
width: 1000px;
|
max-width: 1000px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
.report-actions-button {
|
.report-actions-button {
|
||||||
margin: 3px 0 6px;
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
.report-actions-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.report-card-container {
|
||||||
|
margin: auto;
|
||||||
|
padding: 0 15px;
|
||||||
}
|
}
|
||||||
.report-page-header {
|
.report-page-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -139,4 +162,38 @@ export default {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@media only screen and (max-width:801px) {
|
||||||
|
.report-show-page-container {
|
||||||
|
.id {
|
||||||
|
margin: 7px 15px 15px 15px;
|
||||||
|
}
|
||||||
|
.report-actions-button {
|
||||||
|
margin: 0 3px 6px;
|
||||||
|
}
|
||||||
|
.report-page-header-container {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
.report-page-header {
|
||||||
|
h1 {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
.avatar-name-container {
|
||||||
|
.el-icon-top-right {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.report-page-avatar {
|
||||||
|
margin: 0 5px 0 9px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media only screen and (max-width:480px) {
|
||||||
|
.report-tag {
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in a new issue