Compare commits
6 commits
b1ce85fd33
...
9baa356f56
Author | SHA1 | Date | |
---|---|---|---|
9baa356f56 | |||
|
4406dce589 | ||
|
a57925fce9 | ||
|
3b7bcea826 | ||
|
bc5dcfbafb | ||
|
8b2acaceb5 |
10 changed files with 118 additions and 89 deletions
|
@ -8,6 +8,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
## [2.4.0] - 2021-08-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
- Evicting and banning objects from the MediaProxy cache is disabled if MediaProxy is disabled on the Settings tab. Add ability to enable MediaProxy and Invalidation from MediaProxy tab.
|
- Evicting and banning objects from the MediaProxy cache is disabled if MediaProxy is disabled on the Settings tab. Add ability to enable MediaProxy and Invalidation from MediaProxy tab.
|
||||||
- Allow to upload the custom Terms of Service and Instance Panel HTML pages via Admin API
|
- Allow to upload the custom Terms of Service and Instance Panel HTML pages via Admin API
|
||||||
- Add Report show page and link Moderation log references to the respective reports
|
- Add Report show page and link Moderation log references to the respective reports
|
||||||
|
@ -26,7 +34,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- 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
|
|
||||||
- Remove Websocket based federation settings
|
- Remove Websocket based federation settings
|
||||||
- Move Settings tab navigation from the tabbed menu to the main sidebar menu. A separate route is created for each tab.
|
- Move Settings tab navigation from the tabbed menu to the main sidebar menu. A separate route is created for each tab.
|
||||||
- Move Emoji packs configuration to the Emoji tab in the Settings section
|
- Move Emoji packs configuration to the Emoji tab in the Settings section
|
||||||
|
|
|
@ -48,7 +48,7 @@ const devWebpackConfig = merge(baseWebpackConfig, {
|
||||||
poll: config.dev.poll
|
poll: config.dev.poll
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
'content-security-policy': "script-src 'self' 'unsafe-eval'; base-uri 'self'; frame-ancestors 'none'; img-src 'self' data: https: http:; media-src 'self' https:; style-src 'self' 'unsafe-inline'; font-src 'self'; manifest-src 'self'"
|
'content-security-policy': "base-uri 'self'; frame-ancestors 'none'; img-src 'self' data: https: http:; media-src 'self' https:; style-src 'self' 'unsafe-inline'; font-src 'self'; manifest-src 'self'; script-src 'self';"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -67,12 +67,7 @@ const devWebpackConfig = merge(baseWebpackConfig, {
|
||||||
BASE_URL: devEnv.ASSETS_PUBLIC_PATH + config.dev.assetsSubDirectory,
|
BASE_URL: devEnv.ASSETS_PUBLIC_PATH + config.dev.assetsSubDirectory,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
]
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
vue: 'vue/dist/vue.js'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
module.exports = new Promise((resolve, reject) => {
|
module.exports = new Promise((resolve, reject) => {
|
||||||
|
|
|
@ -29,13 +29,13 @@
|
||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/PanJiaChen/vue-element-admin.git"
|
"url": "git+https://akkoma.dev/AkkomaGang/admin-fe.git"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"prosemirror-model": "1.9.1"
|
"prosemirror-model": "1.9.1"
|
||||||
},
|
},
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/PanJiaChen/vue-element-admin/issues"
|
"url": "https://akkoma.dev/AkkomaGang/admin-fe/-/issues"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.3.4",
|
"@babel/runtime": "^7.3.4",
|
||||||
|
|
|
@ -28,7 +28,7 @@ const getCurrentValue = (type, value, path) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const getValueWithoutKey = (key, [type, value]) => {
|
const getValueWithoutKey = (key, [type, value]) => {
|
||||||
if (prependWithСolon(type, value)) {
|
if (prependWithColon(type, value)) {
|
||||||
return `:${value}`
|
return `:${value}`
|
||||||
} else if (key === ':backends') {
|
} else if (key === ':backends') {
|
||||||
const index = value.findIndex(el => el === ':ex_syslogger')
|
const index = value.findIndex(el => el === ':ex_syslogger')
|
||||||
|
@ -78,14 +78,25 @@ export const parseTuples = (tuples, key) => {
|
||||||
item.tuple[0] === ':replace' ||
|
item.tuple[0] === ':replace' ||
|
||||||
item.tuple[0] === ':retries' ||
|
item.tuple[0] === ':retries' ||
|
||||||
(item.tuple[0] === ':headers' && key === 'Pleroma.Web.MediaProxy.Invalidation.Http') ||
|
(item.tuple[0] === ':headers' && key === 'Pleroma.Web.MediaProxy.Invalidation.Http') ||
|
||||||
item.tuple[0] === ':crontab')) {
|
item.tuple[0] === ':crontab' ||
|
||||||
|
item.tuple[0] === ':transparency_exclusions' ||
|
||||||
|
item.tuple[0] === ':quarantined_instances' ||
|
||||||
|
key === ':mrf_simple')) {
|
||||||
if (item.tuple[0] === ':crontab') {
|
if (item.tuple[0] === ':crontab') {
|
||||||
accum[item.tuple[0]] = item.tuple[1].reduce((acc, group) => {
|
accum[item.tuple[0]] = item.tuple[1].reduce((acc, group) => {
|
||||||
return [...acc, { [group.tuple[1]]: { value: group.tuple[0], id: `f${(~~(Math.random() * 1e8)).toString(16)}` }}]
|
return [...acc, { [group.tuple[1]]: { value: group.tuple[0], id: `f${(~~(Math.random() * 1e8)).toString(16)}` }}]
|
||||||
}, [])
|
}, [])
|
||||||
} else {
|
} else {
|
||||||
accum[item.tuple[0]] = item.tuple[1].reduce((acc, group) => {
|
accum[item.tuple[0]] = item.tuple[1].reduce((acc, group) => {
|
||||||
|
/**
|
||||||
|
* The ':quarantined_instances' and ':mrf_simple' settings have changed to a list of tuples instead of a list of strings.
|
||||||
|
* This is to have backwards compatibility for instances that still use strings.
|
||||||
|
*/
|
||||||
|
if (typeof group === 'string') {
|
||||||
|
return [...acc, group]
|
||||||
|
} else {
|
||||||
return [...acc, { [group.tuple[0]]: { value: group.tuple[1], id: `f${(~~(Math.random() * 1e8)).toString(16)}` }}]
|
return [...acc, { [group.tuple[0]]: { value: group.tuple[1], id: `f${(~~(Math.random() * 1e8)).toString(16)}` }}]
|
||||||
|
}
|
||||||
}, [])
|
}, [])
|
||||||
}
|
}
|
||||||
} else if (item.tuple[0] === ':icons') {
|
} else if (item.tuple[0] === ':icons') {
|
||||||
|
@ -102,8 +113,8 @@ export const parseTuples = (tuples, key) => {
|
||||||
accum[item.tuple[0]] = parseNonTuples(item.tuple[0], item.tuple[1])
|
accum[item.tuple[0]] = parseNonTuples(item.tuple[0], item.tuple[1])
|
||||||
} else if (item.tuple[0] === ':ip_whitelist') {
|
} else if (item.tuple[0] === ':ip_whitelist') {
|
||||||
accum[item.tuple[0]] = item.tuple[1].map(ip => typeof ip === 'string' ? ip : ip.tuple.join('.'))
|
accum[item.tuple[0]] = item.tuple[1].map(ip => typeof ip === 'string' ? ip : ip.tuple.join('.'))
|
||||||
} else if (Array.isArray(item.tuple[1]) &&
|
} else if (Array.isArray(item.tuple[1]) && (item.tuple[1][0] !== null &&
|
||||||
(typeof item.tuple[1][0] === 'object' && !Array.isArray(item.tuple[1][0])) && item.tuple[1][0]['tuple']) {
|
typeof item.tuple[1][0] === 'object' && !Array.isArray(item.tuple[1][0])) && item.tuple[1][0]['tuple']) {
|
||||||
accum[item.tuple[0]] = parseTuples(item.tuple[1], item.tuple[0])
|
accum[item.tuple[0]] = parseTuples(item.tuple[1], item.tuple[0])
|
||||||
} else if (Array.isArray(item.tuple[1])) {
|
} else if (Array.isArray(item.tuple[1])) {
|
||||||
accum[item.tuple[0]] = item.tuple[1]
|
accum[item.tuple[0]] = item.tuple[1]
|
||||||
|
@ -156,7 +167,7 @@ const parseStringOrTupleValue = (key, value) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const prependWithСolon = (type, value) => {
|
const prependWithColon = (type, value) => {
|
||||||
return (type === 'atom' && value.length > 0) ||
|
return (type === 'atom' && value.length > 0) ||
|
||||||
(Array.isArray(type) && type.includes('boolean') && type.includes('atom') && typeof value === 'string')
|
(Array.isArray(type) && type.includes('boolean') && type.includes('atom') && typeof value === 'string')
|
||||||
}
|
}
|
||||||
|
@ -245,7 +256,7 @@ const wrapValues = (settings, currentState) => {
|
||||||
))
|
))
|
||||||
) {
|
) {
|
||||||
return { 'tuple': [setting, wrapValues(value, currentState)] }
|
return { 'tuple': [setting, wrapValues(value, currentState)] }
|
||||||
} else if (prependWithСolon(type, value)) {
|
} else if (prependWithColon(type, value)) {
|
||||||
return { 'tuple': [setting, `:${value}`] }
|
return { 'tuple': [setting, `:${value}`] }
|
||||||
} else if (type.includes('tuple') &&
|
} else if (type.includes('tuple') &&
|
||||||
(type.includes('string') || type.includes('atom') || type.includes('boolean'))) {
|
(type.includes('string') || type.includes('atom') || type.includes('boolean'))) {
|
||||||
|
|
|
@ -55,7 +55,12 @@ export default {
|
||||||
},
|
},
|
||||||
async handleOpen($event) {
|
async handleOpen($event) {
|
||||||
if ($event === '/settings') {
|
if ($event === '/settings') {
|
||||||
if (!localStorage.getItem('settingsTabs')) {
|
let settingsTabs = localStorage.getItem('settingsTabs')
|
||||||
|
if (settingsTabs === '[]') {
|
||||||
|
localStorage.removeItem('settingsTabs')
|
||||||
|
settingsTabs = null
|
||||||
|
}
|
||||||
|
if (!settingsTabs) {
|
||||||
await this.$store.dispatch('FetchSettings')
|
await this.$store.dispatch('FetchSettings')
|
||||||
const menuItems = this.tabs
|
const menuItems = this.tabs
|
||||||
localStorage.setItem('settingsTabs', JSON.stringify(menuItems))
|
localStorage.setItem('settingsTabs', JSON.stringify(menuItems))
|
||||||
|
|
65
src/views/moderationLog/LogEntryMessage.vue
Normal file
65
src/views/moderationLog/LogEntryMessage.vue
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<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>
|
|
@ -1,20 +0,0 @@
|
||||||
<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>
|
|
|
@ -1,20 +0,0 @@
|
||||||
<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,7 +43,8 @@
|
||||||
v-for="(logEntry, index) in log"
|
v-for="(logEntry, index) in log"
|
||||||
:key="index"
|
:key="index"
|
||||||
:timestamp="normalizeTimestamp(logEntry.time)">
|
:timestamp="normalizeTimestamp(logEntry.time)">
|
||||||
<component :is="processedMessage(logEntry)"/>
|
<log-entry-message v-if="propertyExists(logEntry.data.actor, 'nickname')" :actor="logEntry.data.actor" :message="logEntry.message" :subject="logEntry.data.subject"/>
|
||||||
|
<span v-else>{{ logEntry.message }}</span>
|
||||||
</el-timeline-item>
|
</el-timeline-item>
|
||||||
</el-timeline>
|
</el-timeline>
|
||||||
<div class="pagination">
|
<div class="pagination">
|
||||||
|
@ -64,14 +65,10 @@ 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 ReportLink from './ReportLink'
|
import LogEntryMessage from './LogEntryMessage'
|
||||||
import UserLink from './UserLink'
|
|
||||||
import Vue from 'vue'
|
|
||||||
Vue.component('user-link', UserLink)
|
|
||||||
Vue.component('report-link', ReportLink)
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { RebootButton },
|
components: { RebootButton, LogEntryMessage },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
dateRange: '',
|
dateRange: '',
|
||||||
|
@ -133,24 +130,6 @@ 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]
|
||||||
}
|
}
|
||||||
|
@ -194,9 +173,6 @@ h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 145px;
|
width: 145px;
|
||||||
}
|
}
|
||||||
.router-link {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.pagination {
|
.pagination {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,12 @@ export default {
|
||||||
return this.$store.state.app.device === 'desktop'
|
return this.$store.state.app.device === 'desktop'
|
||||||
},
|
},
|
||||||
keyPlaceholder() {
|
keyPlaceholder() {
|
||||||
return this.setting.key === ':replace' ? 'pattern' : 'key'
|
/**
|
||||||
|
* We can get 'key_placeholder' from the Pleroma BE. This wasn't always the case.
|
||||||
|
* We check for the key ':replace' for backwards compatibility for older Pleroma instances who didn't send 'key_placeholder' yet.
|
||||||
|
* The ':replace' key was the only key where this was needed.
|
||||||
|
*/
|
||||||
|
return this.setting.key_placeholder ? this.setting.key_placeholder : (this.setting.key === ':replace' ? 'pattern' : 'key')
|
||||||
},
|
},
|
||||||
settings() {
|
settings() {
|
||||||
return this.$store.state.settings.settings
|
return this.$store.state.settings.settings
|
||||||
|
@ -96,7 +101,12 @@ export default {
|
||||||
return this.$store.state.settings.updatedSettings
|
return this.$store.state.settings.updatedSettings
|
||||||
},
|
},
|
||||||
valuePlaceholder() {
|
valuePlaceholder() {
|
||||||
return this.setting.key === ':replace' ? 'replacement' : 'value'
|
/**
|
||||||
|
* We can get 'value_placeholder' from the Pleroma BE. This wasn't always the case.
|
||||||
|
* We check for the key ':replace' for backwards compatibility for older Pleroma instances who didn't send 'value_placeholder' yet.
|
||||||
|
* The ':replace' key was the only key where this was needed.
|
||||||
|
*/
|
||||||
|
return this.setting.value_placeholder ? this.setting.value_placeholder : (this.setting.key === ':replace' ? 'replacement' : 'value')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
Loading…
Reference in a new issue