Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • pleroma/admin-fe
  • linafilippova/admin-fe
  • Exilat_a_Tolosa/admin-fe
  • mkljczk/admin-fe
  • maxf/admin-fe
  • kphrx/admin-fe
  • vaartis/admin-fe
  • ELR/admin-fe
  • eugenijm/admin-fe
  • jp/admin-fe
  • mkfain/admin-fe
  • lorenzoancora/admin-fe
  • alexgleason/admin-fe
  • seanking/admin-fe
  • ilja/admin-fe
15 results
Show changes
Showing
with 543 additions and 135 deletions
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>text-underline</title><path d="M22.5,21.248H1.5a1.25,1.25,0,0,0,0,2.5h21a1.25,1.25,0,0,0,0-2.5Z"/><path d="M1.978,2.748H3.341a.25.25,0,0,1,.25.25v8.523a8.409,8.409,0,0,0,16.818,0V3a.25.25,0,0,1,.25-.25h1.363a1.25,1.25,0,0,0,0-2.5H16.3a1.25,1.25,0,0,0,0,2.5h1.363a.25.25,0,0,1,.25.25v8.523a5.909,5.909,0,0,1-11.818,0V3a.25.25,0,0,1,.25-.25H7.7a1.25,1.25,0,1,0,0-2.5H1.978a1.25,1.25,0,0,0,0,2.5Z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>undo</title><path d="M17.786,3.77A12.542,12.542,0,0,0,4.821,2.905a.249.249,0,0,1-.292-.045L1.937.269A.507.507,0,0,0,1.392.16a.5.5,0,0,0-.308.462v6.7a.5.5,0,0,0,.5.5h6.7a.5.5,0,0,0,.354-.854L6.783,5.115a.253.253,0,0,1-.068-.228.249.249,0,0,1,.152-.181,10,10,0,0,1,9.466,1.1,9.759,9.759,0,0,1,.094,15.809A1.25,1.25,0,0,0,17.9,23.631a12.122,12.122,0,0,0,5.013-9.961A12.125,12.125,0,0,0,17.786,3.77Z"/></svg>
......@@ -103,7 +103,11 @@ export default {
evictObjectsHeader: 'Evict object from the MediaProxy cache',
listBannedUrlsHeader: 'List of all banned MediaProxy URLs',
multipleInput: 'You can enter a single URL or several comma separated links',
removeSelected: 'Remove Selected'
removeSelected: 'Remove Selected',
enable: 'Enable',
invalidationAndMediaProxy: 'MediaProxy and Invalidation to evict and ban MediaProxy objects',
confirmEnablingMediaProxy: 'Are you sure you want to enable Media Proxy and Media Cache object Invalidation?',
enableMediaProxySuccessMessage: 'Media Proxy and Media Cache object Invalidation were enabled'
},
documentation: {
documentation: 'Documentation',
......@@ -273,7 +277,10 @@ export default {
linkToResetPassword: 'You can also use this link to reset password:',
registrationReason: 'Registration Reason',
service: 'Service',
person: 'Person'
person: 'Person',
enableTagPolicy: 'Enable MRF TagPolicy to manage user tags',
confirmEnablingTagPolicy: 'Are you sure you want to add TagPolicy to the list of enabled MRF policies?',
enableTagPolicySuccessMessage: 'MRF TagPolicy was enabled'
},
statuses: {
statuses: 'Statuses',
......@@ -429,7 +436,9 @@ export default {
removeSettingConfirmation: 'Are you sure you want to remove this setting\'s value from the database?',
changeImage: 'Change image',
uploadImage: 'Upload image',
remove: 'Remove'
remove: 'Remove',
instancePanel: 'Instance Panel Document',
termsOfServices: 'Terms of Service'
},
invites: {
inviteTokens: 'Invite tokens',
......@@ -476,6 +485,7 @@ export default {
homepage: 'Homepage',
description: 'Description',
packs: 'Packs',
displayName: 'Display name',
license: 'License',
shortcode: 'Shortcode',
fallbackSrc: 'Fallback source',
......@@ -511,6 +521,9 @@ export default {
specifyFilename: 'Specify a custom filename',
copy: 'Copy',
copyToLocalPack: 'Copy to local pack',
emptyPack: 'This emoji pack is empty'
emptyPack: 'This emoji pack is empty',
emojiWarning: 'Pack names cannot include any of the following characters: # / < > & +',
image: 'Image'
}
}
......@@ -20,25 +20,29 @@ import Vue from 'vue'
const emojiPacks = {
state: {
activeTab: '',
currentFilesPage: 1,
currentPage: 1,
currentLocalFilesPage: 1,
currentLocalPacksPage: 1,
currentRemoteFilesPage: 1,
currentRemotePacksPage: 1,
filesPageSize: 30,
localPackFilesCount: 0,
localPacks: {},
localPacksCount: 0,
pageSize: 50,
remoteInstance: '',
remotePacks: {}
remotePackFilesCount: 0,
remotePacks: {},
remotePacksCount: 0
},
mutations: {
SET_ACTIVE_TAB: (state, tab) => {
state.activeTab = tab
},
SET_FILES_COUNT: (state, count) => {
SET_LOCAL_FILES_COUNT: (state, count) => {
state.localPackFilesCount = count
},
SET_FILES_PAGE: (state, page) => {
state.currentFilesPage = page
SET_LOCAL_FILES_PAGE: (state, page) => {
state.currentLocalFilesPage = page
},
SET_LOCAL_PACKS: (state, packs) => {
state.localPacks = packs
......@@ -46,15 +50,27 @@ const emojiPacks = {
SET_LOCAL_PACKS_COUNT: (state, count) => {
state.localPacksCount = count
},
SET_PACK_FILES: (state, { name, files }) => {
SET_LOCAL_PACK_FILES: (state, { name, files }) => {
state.localPacks = { ...state.localPacks, [name]: { ...state.localPacks[name], files }}
},
SET_PAGE: (state, page) => {
state.currentPage = page
SET_LOCAL_PAGE: (state, page) => {
state.currentLocalPacksPage = page
},
SET_REMOTE_FILES_COUNT: (state, count) => {
state.remotePackFilesCount = count
},
SET_REMOTE_FILES_PAGE: (state, page) => {
state.currentRemoteFilesPage = page
},
SET_REMOTE_INSTANCE: (state, name) => {
state.remoteInstance = name
},
SET_REMOTE_PACKS_COUNT: (state, count) => {
state.remotePacksCount = count
},
SET_REMOTE_PACK_FILES: (state, { name, files }) => {
state.remotePacks = { ...state.remotePacks, [name]: { ...state.remotePacks[name], files }}
},
SET_REMOTE_PACKS: (state, packs) => {
state.remotePacks = packs
},
......@@ -103,10 +119,10 @@ const emojiPacks = {
type: 'success',
duration: 5 * 1000
})
if (Object.keys(updatedPackFiles).length === 0 && state.currentFilesPage > 1) {
dispatch('FetchSinglePack', { name: packName, page: state.currentFilesPage - 1 })
if (Object.keys(updatedPackFiles).length === 0 && state.currentLocalFilesPage > 1) {
dispatch('FetchLocalSinglePack', { name: packName, page: state.currentLocalFilesPage - 1 })
} else {
dispatch('FetchSinglePack', { name: packName, page: state.currentFilesPage })
dispatch('FetchLocalSinglePack', { name: packName, page: state.currentLocalFilesPage })
}
},
async CreatePack({ getters }, { name }) {
......@@ -136,14 +152,21 @@ const emojiPacks = {
}, {})
commit('SET_LOCAL_PACKS', updatedPacks)
commit('SET_LOCAL_PACKS_COUNT', count)
commit('SET_PAGE', page)
commit('SET_LOCAL_PAGE', page)
},
async FetchLocalSinglePack({ getters, commit, state }, { name, page }) {
const { data } = await fetchPack(name, page, state.filesPageSize, getters.authHost, getters.token)
const { files, files_count } = data
commit('SET_LOCAL_PACK_FILES', { name, files })
commit('SET_LOCAL_FILES_COUNT', files_count)
commit('SET_LOCAL_FILES_PAGE', page)
},
async FetchSinglePack({ getters, commit, state }, { name, page }) {
async FetchRemoteSinglePack({ getters, commit, state }, { name, page }) {
const { data } = await fetchPack(name, page, state.filesPageSize, getters.authHost, getters.token)
const { files, files_count } = data
commit('SET_PACK_FILES', { name, files })
commit('SET_FILES_COUNT', files_count)
commit('SET_FILES_PAGE', page)
commit('SET_REMOTE_PACK_FILES', { name, files })
commit('SET_REMOTE_FILES_COUNT', files_count)
commit('SET_REMOTE_FILES_PAGE', page)
},
async ImportFromFS({ getters }) {
const result = await importFromFS(getters.authHost, getters.token)
......@@ -185,11 +208,18 @@ const emojiPacks = {
SetActiveTab({ commit }, activeTab) {
commit('SET_ACTIVE_TAB', activeTab)
},
async SetRemoteEmojiPacks({ commit, getters }, { remoteInstance }) {
const { data } = await listRemotePacks(getters.authHost, getters.token, remoteInstance)
async SetRemoteEmojiPacks({ commit, getters, state }, { page, remoteInstance }) {
const { data } = await listRemotePacks(remoteInstance, page, state.pageSize, getters.authHost, getters.token)
const { packs, count } = data
const updatedPacks = Object.keys(packs).reduce((acc, packName) => {
const { files, ...pack } = packs[packName]
acc[packName] = pack
return acc
}, {})
commit('SET_REMOTE_INSTANCE', remoteInstance)
commit('SET_REMOTE_PACKS', data.packs)
commit('SET_REMOTE_PACKS', updatedPacks)
commit('SET_REMOTE_PACKS_COUNT', count)
},
SetRemoteInstance({ commit }, instance) {
commit('SET_REMOTE_INSTANCE', instance)
......@@ -216,7 +246,7 @@ const emojiPacks = {
duration: 5 * 1000
})
dispatch('FetchSinglePack', { name: packName, page: state.currentFilesPage })
dispatch('FetchLocalSinglePack', { name: packName, page: state.currentLocalFilesPage })
},
async UpdateLocalPackVal({ commit }, args) {
commit('UPDATE_LOCAL_PACK_VAL', args)
......
import { listBannedUrls, purgeUrls, removeBannedUrls, searchBannedUrls } from '@/api/mediaProxyCache'
import { fetchSettings, updateSettings } from '@/api/settings'
import { Message } from 'element-ui'
import i18n from '@/lang'
......@@ -7,10 +8,15 @@ const mediaProxyCache = {
bannedUrls: [],
currentPage: 1,
loading: false,
mediaProxyEnabled: false,
pageSize: 50,
searchQuery: '',
totalUrlsCount: 0
},
mutations: {
MEDIA_PROXY_ENABLED: (state, enabled) => {
state.mediaProxyEnabled = enabled
},
SET_BANNED_URLS: (state, urls) => {
state.bannedUrls = urls.map(el => { return { url: el } })
},
......@@ -22,9 +28,36 @@ const mediaProxyCache = {
},
SET_PAGE: (state, page) => {
state.currentPage = page
},
SET_SEARCH_QUERY: (state, query) => {
state.searchQuery = query
}
},
actions: {
async EnableMediaProxy({ dispatch, getters, state }) {
const configs = [{
group: ':pleroma',
key: ':media_proxy',
value: [
{ tuple: [':enabled', true] },
{ tuple: [':invalidation', [{ tuple: [':enabled', true] }]] }
]
}]
await updateSettings(configs, getters.authHost, getters.token)
dispatch('FetchMediaProxySetting')
},
async FetchMediaProxySetting({ commit, getters }) {
const { data } = await fetchSettings(getters.authHost, getters.token)
const mediaProxySettings = data.configs.find(el => el.key === ':media_proxy')
? data.configs.find(el => el.key === ':media_proxy').value
: []
const mediaProxyEnabled = mediaProxySettings.find(el => el.tuple[0] === ':enabled')
? mediaProxySettings.find(el => el.tuple[0] === ':enabled').tuple[1]
: false
commit('MEDIA_PROXY_ENABLED', mediaProxyEnabled)
},
async ListBannedUrls({ commit, getters, state }, { page }) {
commit('SET_LOADING', true)
const response = await listBannedUrls(page, state.pageSize, getters.authHost, getters.token)
......@@ -40,13 +73,19 @@ const mediaProxyCache = {
type: 'success',
duration: 5 * 1000
})
if (ban) {
if (ban && state.searchQuery.length === 0) {
dispatch('ListBannedUrls', { page: state.currentPage })
} else if (ban) {
dispatch('SearchUrls', { query: state.searchQuery, page: state.currentPage })
}
},
async RemoveBannedUrls({ dispatch, getters, state }, urls) {
await removeBannedUrls(urls, getters.authHost, getters.token)
dispatch('ListBannedUrls', { page: state.currentPage })
if (state.searchQuery.length === 0) {
dispatch('ListBannedUrls', { page: state.currentPage })
} else {
dispatch('SearchUrls', { query: state.searchQuery, page: state.currentPage })
}
},
async SearchUrls({ commit, dispatch, getters, state }, { query, page }) {
if (query.length === 0) {
......
......@@ -66,7 +66,11 @@ export const parseTuples = (tuples, key) => {
return [...acc, { [mascot.tuple[0]]: { ...mascot.tuple[1], id: `f${(~~(Math.random() * 1e8)).toString(16)}` }}]
}, [])
} else if (Array.isArray(item.tuple[1]) &&
(item.tuple[0] === ':groups' || item.tuple[0] === ':replace' || item.tuple[0] === ':retries' || item.tuple[0] === ':headers' || item.tuple[0] === ':crontab')) {
(item.tuple[0] === ':groups' ||
item.tuple[0] === ':replace' ||
item.tuple[0] === ':retries' ||
(item.tuple[0] === ':headers' && key === 'Pleroma.Web.MediaProxy.Invalidation.Http') ||
item.tuple[0] === ':crontab')) {
if (item.tuple[0] === ':crontab') {
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)}` }}]
......@@ -245,7 +249,7 @@ const wrapValues = (settings, currentState) => {
return acc
}, {})
return { 'tuple': [setting, { ...currentState[setting], ...mapValue }] }
} else if (type.includes('map')) {
} else if (type.includes('map') && !type.includes('list')) {
const mapValue = Object.keys(value).reduce((acc, key) => {
acc[key] = value[key][1]
return acc
......@@ -271,7 +275,7 @@ export const formSearchObject = description => {
return [...acc, resultObject]
}, [])
return description.reduce((acc, setting) => {
const processedDescription = description.reduce((acc, setting) => {
const searchArray = _.compact([setting.key, setting.label, setting.description]).map(el => el.toLowerCase())
const resultObject = { label: setting.label, key: setting.key || setting.group, groupKey: setting.key || setting.group, groupLabel: setting.label, search: searchArray }
if (setting.children) {
......@@ -280,4 +284,19 @@ export const formSearchObject = description => {
}
return !setting.key && setting.group === ':pleroma' ? acc : [...acc, resultObject]
}, [])
const searchDataForEditableDocs = [{
groupKey: ':instance_panel',
groupLabel: 'Instance Panel',
key: ':instance_panel',
label: 'Instance Panel',
search: ['Instance Panel', ':instance_panel']
}, {
groupKey: ':terms_of_services',
groupLabel: 'Terms of Services',
key: ':terms_of_services',
label: 'Terms of Services',
search: ['Terms of Services', ':terms_of_services']
}]
return processedDescription.concat(searchDataForEditableDocs)
}
import { changeState, fetchReports, createNote, deleteNote } from '@/api/reports'
import {
activateUsers,
deactivateUsers,
deleteUsers,
tagUser,
untagUser
} from '@/api/users'
const reports = {
state: {
......@@ -34,6 +41,34 @@ const reports = {
}
},
actions: {
async ActivateUserFromReports({ commit, dispatch, getters, state }, { user, reportId }) {
try {
await activateUsers([user.nickname], getters.authHost, getters.token)
} catch (_e) {
return
} finally {
const updatedReports = state.fetchedReports.map(report => {
const updatedAccount = { ...user, deactivated: false }
return report.id === reportId ? { ...report, account: updatedAccount } : report
})
commit('SET_REPORTS', updatedReports)
}
dispatch('SuccessMessage')
},
async AddTagFromReports({ commit, dispatch, getters, state }, { user, tag, reportId }) {
try {
await tagUser([user.nickname], [tag], getters.authHost, getters.token)
} catch (_e) {
return
} finally {
const updatedReports = state.fetchedReports.map(report => {
const updatedAccount = { ...user, tags: [...user.tags, tag] }
return report.id === reportId ? { ...report, account: updatedAccount } : report
})
commit('SET_REPORTS', updatedReports)
}
dispatch('SuccessMessage')
},
async ChangeReportState({ commit, dispatch, getters, state }, reportsData) {
changeState(reportsData, getters.authHost, getters.token)
......@@ -48,6 +83,34 @@ const reports = {
ClearFetchedReports({ commit }) {
commit('SET_REPORTS', [])
},
async DeactivateUserFromReports({ commit, dispatch, getters, state }, { user, reportId }) {
try {
await deactivateUsers([user.nickname], getters.authHost, getters.token)
} catch (_e) {
return
} finally {
const updatedReports = state.fetchedReports.map(report => {
const updatedAccount = { ...user, deactivated: true }
return report.id === reportId ? { ...report, account: updatedAccount } : report
})
commit('SET_REPORTS', updatedReports)
}
dispatch('SuccessMessage')
},
async DeleteUserFromReports({ commit, dispatch, getters, state }, { user, reportId }) {
try {
await deleteUsers([user.nickname], getters.authHost, getters.token)
} catch (_e) {
return
} finally {
const updatedReports = state.fetchedReports.map(report => {
const updatedAccount = { ...user, deactivated: true }
return report.id === reportId ? { ...report, account: updatedAccount } : report
})
commit('SET_REPORTS', updatedReports)
}
dispatch('SuccessMessage')
},
async FetchReports({ commit, getters, state }, page) {
commit('SET_LOADING', true)
const { data } = await fetchReports(state.stateFilter, page, state.pageSize, getters.authHost, getters.token)
......@@ -64,6 +127,20 @@ const reports = {
commit('SET_OPEN_REPORTS_COUNT', data.total)
commit('SET_LOADING', false)
},
async RemoveTagFromReports({ commit, dispatch, getters, state }, { user, tag, reportId }) {
try {
await untagUser([user.nickname], [tag], getters.authHost, getters.token)
} catch (_e) {
return
} finally {
const updatedReports = state.fetchedReports.map(report => {
const updatedAccount = { ...user, tags: user.tags.filter(userTag => userTag !== tag) }
return report.id === reportId ? { ...report, account: updatedAccount } : report
})
commit('SET_REPORTS', updatedReports)
}
dispatch('SuccessMessage')
},
SetReportsFilter({ commit }, filter) {
commit('SET_REPORTS_FILTER', filter)
},
......
import { fetchDescription, fetchSettings, removeSettings, updateSettings } from '@/api/settings'
import {
deleteInstanceDocument,
fetchDescription,
fetchSettings,
getInstanceDocument,
removeSettings,
updateInstanceDocument,
updateSettings } from '@/api/settings'
import { formSearchObject, parseNonTuples, parseTuples, valueHasTuples, wrapUpdatedSettings } from './normalizers'
import _ from 'lodash'
......@@ -8,15 +15,20 @@ const settings = {
configDisabled: true,
db: {},
description: [],
instancePanel: '',
loading: true,
searchData: {},
settings: {},
termsOfServices: '',
updatedSettings: {}
},
mutations: {
CLEAR_UPDATED_SETTINGS: (state) => {
state.updatedSettings = {}
},
SET_INSTANCE_PANEL: (state, data) => {
state.instancePanel = data
},
REMOVE_SETTING_FROM_UPDATED: (state, { group, key, subkeys }) => {
if (_.get(state.updatedSettings, [group, key, subkeys[0]])) {
const { [subkeys[0]]: value, ...updatedSettings } = state.updatedSettings[group][key]
......@@ -54,6 +66,9 @@ const settings = {
state.settings = newSettings
state.db = newDbSettings
},
SET_TERMS_OF_SERVICES: (state, data) => {
state.termsOfServices = data
},
TOGGLE_TABS: (state, status) => {
state.configDisabled = status
},
......@@ -71,6 +86,14 @@ const settings = {
}
},
actions: {
async FetchInstanceDocument({ commit, getters }, name) {
const { data } = await getInstanceDocument(name, getters.authHost, getters.token)
if (name === 'instance-panel') {
commit('SET_INSTANCE_PANEL', data)
} else {
commit('SET_TERMS_OF_SERVICES', data)
}
},
async FetchSettings({ commit, getters }) {
commit('SET_LOADING', true)
try {
......@@ -89,6 +112,10 @@ const settings = {
commit('TOGGLE_TABS', false)
commit('SET_LOADING', false)
},
async RemoveInstanceDocument({ dispatch, getters }, name) {
await deleteInstanceDocument(name, getters.authHost, getters.token)
await dispatch('FetchInstanceDocument', name)
},
async RemoveSetting({ commit, getters }, configs) {
await removeSettings(configs, getters.authHost, getters.token)
const response = await fetchSettings(getters.authHost, getters.token)
......@@ -111,6 +138,13 @@ const settings = {
commit('TOGGLE_REBOOT', response.data.need_reboot)
commit('CLEAR_UPDATED_SETTINGS')
},
async UpdateInstanceDocs({ commit, getters }, { name, content }) {
commit('SET_INSTANCE_PANEL', content)
const formData = new FormData()
const blob = new Blob([content], { type: 'text/html' })
formData.append('file', blob)
await updateInstanceDocument(name, formData, getters.authHost, getters.token)
},
UpdateSettings({ commit }, { group, key, input, value, type }) {
key
? commit('UPDATE_SETTINGS', { group, key, input, value, type })
......
......@@ -19,12 +19,14 @@ import {
resendConfirmationEmail,
updateUserCredentials
} from '@/api/users'
import { fetchSettings, updateSettings } from '@/api/settings'
const users = {
state: {
fetchedUsers: [],
loading: true,
searchQuery: '',
mrfPolicies: [],
totalUsersCount: 0,
currentPage: 1,
pageSize: 50,
......@@ -78,6 +80,9 @@ const users = {
SET_SEARCH_QUERY: (state, query) => {
state.searchQuery = query
},
SET_TAG_POLICY: (state, mrfPolicies) => {
state.mrfPolicies = mrfPolicies
},
SET_USERS_FILTERS: (state, filters) => {
state.filters = filters
}
......@@ -206,6 +211,27 @@ const users = {
}
dispatch('SuccessMessage')
},
async EnableTagPolicy({ dispatch, getters, state }) {
const configs = [{
group: ':pleroma',
key: ':mrf',
value: [{ tuple: [':policies', [...state.mrfPolicies, 'Pleroma.Web.ActivityPub.MRF.TagPolicy']] }]
}]
await updateSettings(configs, getters.authHost, getters.token)
dispatch('FetchTagPolicySetting')
},
async FetchTagPolicySetting({ commit, getters }) {
const { data } = await fetchSettings(getters.authHost, getters.token)
const mrfSettings = data.configs.find(el => el.key === ':mrf')
? data.configs.find(el => el.key === ':mrf').value
: []
const mrfPolicies = mrfSettings.find(el => el.tuple[0] === ':policies')
? mrfSettings.find(el => el.tuple[0] === ':policies').tuple[1]
: []
commit('SET_TAG_POLICY', Array.isArray(mrfPolicies) ? mrfPolicies : [mrfPolicies])
},
async FetchUsers({ commit, dispatch, getters, state }, { page }) {
commit('SET_LOADING', true)
const filters = Object.keys(state.filters).filter(filter => state.filters[filter]).join()
......
......@@ -101,7 +101,6 @@ div:focus {
code {
background: #eef1f6;
padding: 15px 16px;
margin-bottom: 20px;
display: block;
line-height: 36px;
font-size: 15px;
......
......@@ -4,6 +4,9 @@
<el-form-item :label=" $t('emoji.sharePack')">
<el-switch v-model="share" />
</el-form-item>
<el-form-item :label=" $t('emoji.displayName')">
<el-input v-model="displayName" />
</el-form-item>
<el-form-item :label=" $t('emoji.homepage')">
<el-input v-model="homepage" />
</el-form-item>
......@@ -30,7 +33,7 @@
<div class="download-pack-button-container">
<el-link
v-if="pack.pack['can-download']"
:href="`//${host}/api/pleroma/emoji/packs/${name}/download_shared`"
:href="`//${host}//api/pleroma/emoji/packs/archive?name=${name}`"
:underline="false"
type="primary"
target="_blank">
......@@ -44,6 +47,11 @@
</el-collapse-item>
<el-collapse-item :title=" $t('emoji.manageEmoji')" name="manageEmoji" class="no-background">
<div v-if="pack.files && Object.keys(pack.files).length > 0">
<div :class="isMobile ? 'emoji-container-flex' : 'emoji-container-grid'">
<span class="emoji-preview-img emoji-table-head">{{ $t('emoji.image') }}</span>
<span class="emoji-table-head">{{ $t('emoji.shortcode') }}</span>
<span class="emoji-table-head">{{ $t('emoji.file') }}</span>
</div>
<single-emoji-editor
v-for="(file, shortcode) in pack.files"
:key="shortcode"
......@@ -100,10 +108,10 @@ export default {
},
computed: {
currentFilesPage() {
return this.$store.state.emojiPacks.currentFilesPage
return this.$store.state.emojiPacks.currentLocalFilesPage
},
currentPage() {
return this.$store.state.emojiPacks.currentPage
currentLocalPacksPage() {
return this.$store.state.emojiPacks.currentLocalPacksPage
},
isMobile() {
return this.$store.state.app.device === 'mobile'
......@@ -144,6 +152,15 @@ export default {
)
}
},
displayName: {
get() { return this.pack.pack['display-name'] },
set(value) {
this.$store.dispatch(
'UpdateLocalPackVal',
{ name: this.name, key: 'display-name', value }
)
}
},
description: {
get() { return this.pack.pack['description'] },
set(value) {
......@@ -197,21 +214,21 @@ export default {
.then(() => this.$store.dispatch('ReloadEmoji'))
.then(() => {
const { [this.name]: value, ...updatedPacks } = this.$store.state.emojiPacks.localPacks
if (Object.keys(updatedPacks).length === 0 && this.currentPage > 1) {
this.$store.dispatch('FetchLocalEmojiPacks', this.currentPage - 1)
if (Object.keys(updatedPacks).length === 0 && this.currentLocalPacksPage > 1) {
this.$store.dispatch('FetchLocalEmojiPacks', this.currentLocalPacksPage - 1)
} else {
this.$store.dispatch('FetchLocalEmojiPacks', this.currentPage)
this.$store.dispatch('FetchLocalEmojiPacks', this.currentLocalPacksPage)
}
})
}).catch(() => {})
},
handleChange(openTabs, name) {
if (openTabs.includes('manageEmoji')) {
this.$store.dispatch('FetchSinglePack', { name, page: 1 })
this.$store.dispatch('FetchLocalSinglePack', { name, page: 1 })
}
},
handleFilesPageChange(page) {
this.$store.dispatch('FetchSinglePack', { name: this.name, page })
this.$store.dispatch('FetchLocalSinglePack', { name: this.name, page })
},
savePackMetadata() {
this.$store.dispatch('SavePackMetadata', { packName: this.name })
......@@ -252,6 +269,11 @@ export default {
font-weight: 700;
color: #606266;
}
.emoji-table-head {
color: #909399;
font-size: 14px;
font-weight: 700;
}
.emoji-pack-card {
margin-top: 5px;
}
......
......@@ -33,7 +33,7 @@
</el-link>
</el-form-item>
</el-form>
<el-collapse v-model="showPackContent" class="contents-collapse">
<el-collapse v-model="showPackContent" class="contents-collapse" @change="handleChange($event, name)">
<el-collapse-item :title=" $t('emoji.manageEmoji')" name="manageEmoji" class="no-background">
<div v-if="pack.files && Object.keys(pack.files).length > 0">
<single-emoji-editor
......@@ -46,6 +46,16 @@
:is-local="isLocal" />
</div>
<span v-else class="expl">{{ $t('emoji.emptyPack') }}</span>
<div class="files-pagination">
<el-pagination
:total="remotePackFilesCount"
:current-page="currentFilesPage"
:page-size="pageSize"
hide-on-single-page
layout="prev, pager, next"
@current-change="handleFilesPageChange"
/>
</div>
</el-collapse-item>
<el-collapse-item :title=" $t('emoji.downloadPack')" name="downloadPack" class="no-background">
<p>
......@@ -95,8 +105,11 @@ export default {
}
},
computed: {
currentPage() {
return this.$store.state.emojiPacks.currentPage
currentFilesPage() {
return this.$store.state.emojiPacks.currentRemoteFilesPage
},
currentRemotePacksPage() {
return this.$store.state.emojiPacks.currentRemotePacksPage
},
isDesktop() {
return this.$store.state.app.device === 'desktop'
......@@ -119,9 +132,15 @@ export default {
loadRemotePack() {
return this.$store.state.emojiPacks.activeTab === this.name
},
pageSize() {
return this.$store.state.emojiPacks.filesPageSize
},
remoteInstanceAddress() {
return this.$store.state.emojiPacks.remoteInstance
},
remotePackFilesCount() {
return this.$store.state.emojiPacks.remotePackFilesCount
},
share: {
get() { return this.pack.pack['share-files'] },
set(value) {
......@@ -186,6 +205,14 @@ export default {
{ instanceAddress: this.remoteInstanceAddress, packName: this.name, as: this.downloadSharedAs }
).then(() => this.$store.dispatch('ReloadEmoji'))
.then(() => this.$store.dispatch('FetchLocalEmojiPacks', this.currentPage))
},
handleChange(openTabs, name) {
if (openTabs.includes('manageEmoji')) {
this.$store.dispatch('FetchRemoteSinglePack', { name, page: 1 })
}
},
handleFilesPageChange(page) {
this.$store.dispatch('FetchRemoteSinglePack', { name: this.name, page })
}
}
}
......@@ -231,6 +258,10 @@ export default {
margin-bottom: 10px;
}
}
.files-pagination {
margin: 25px 0;
text-align: center;
}
.has-background .el-collapse-item__header {
background: #f6f6f6;
}
......
......@@ -178,7 +178,7 @@ export default {
}
.emoji-container-grid {
display: grid;
grid-template-columns: 75px auto auto 200px;
grid-template-columns: 75px 1fr 1fr 200px;
grid-column-gap: 15px;
margin-bottom: 10px;
}
......@@ -197,7 +197,7 @@ export default {
}
.remote-emoji-container-grid {
display: grid;
grid-template-columns: 75px auto auto 160px;
grid-template-columns: 75px 1fr 1fr 160px;
grid-column-gap: 15px;
margin-bottom: 10px;
}
......
......@@ -30,6 +30,7 @@
{{ $t('users.create') }}
</el-button>
</div>
<span class="emoji-name-warning">{{ $t('emoji.emojiWarning') }}</span>
</el-form-item>
<el-form-item v-if="Object.keys(localPacks).length > 0" :label="$t('emoji.packs')">
<el-collapse v-for="(pack, name) in localPacks" :key="name" v-model="activeLocalPack" accordion @change="setActiveTab">
......@@ -40,11 +41,11 @@
<div class="pagination">
<el-pagination
:total="localPacksCount"
:current-page="currentPage"
:current-page="currentLocalPacksPage"
:page-size="pageSize"
hide-on-single-page
layout="prev, pager, next"
@current-change="handlePageChange"
@current-change="handleLocalPageChange"
/>
</div>
</el-tab-pane>
......@@ -70,6 +71,16 @@
</el-collapse>
</el-form-item>
</el-form>
<div class="pagination">
<el-pagination
:total="remotePacksCount"
:current-page="currentRemotePacksPage"
:page-size="pageSize"
hide-on-single-page
layout="prev, pager, next"
@current-change="handleRemotePageChange"
/>
</div>
</el-tab-pane>
</el-tabs>
</div>
......@@ -93,8 +104,11 @@ export default {
}
},
computed: {
currentPage() {
return this.$store.state.emojiPacks.currentPage
currentLocalPacksPage() {
return this.$store.state.emojiPacks.currentLocalPacksPage
},
currentRemotePacksPage() {
return this.$store.state.emojiPacks.currentRemotePacksPage
},
isMobile() {
return this.$store.state.app.device === 'mobile'
......@@ -130,6 +144,9 @@ export default {
},
remotePacks() {
return this.$store.state.emojiPacks.remotePacks
},
remotePacksCount() {
return this.$store.state.emojiPacks.remotePacksCount
}
},
mounted() {
......@@ -143,23 +160,26 @@ export default {
.then(() => {
this.newPackName = ''
this.$store.dispatch('FetchLocalEmojiPacks', this.currentPage)
this.$store.dispatch('FetchLocalEmojiPacks', this.currentLocalPacksPage)
this.$store.dispatch('ReloadEmoji')
})
},
handlePageChange(page) {
handleLocalPageChange(page) {
this.$store.dispatch('FetchLocalEmojiPacks', page)
},
handleRemotePageChange(page) {
this.$store.dispatch('SetRemoteEmojiPacks', { page, remoteInstance: this.remoteInstanceAddress })
},
importFromFS() {
this.$store.dispatch('ImportFromFS')
.then(() => {
this.$store.dispatch('FetchLocalEmojiPacks', this.currentPage)
this.$store.dispatch('FetchLocalEmojiPacks', this.currentLocalPacksPage)
this.$store.dispatch('ReloadEmoji')
})
},
refreshLocalPacks() {
try {
this.$store.dispatch('FetchLocalEmojiPacks', this.currentPage)
this.$store.dispatch('FetchLocalEmojiPacks', this.currentLocalPacksPage)
} catch (e) {
return
}
......@@ -170,7 +190,7 @@ export default {
},
async refreshRemotePacks() {
this.fullscreenLoading = true
await this.$store.dispatch('SetRemoteEmojiPacks', { remoteInstance: this.remoteInstanceAddress })
await this.$store.dispatch('SetRemoteEmojiPacks', { page: 1, remoteInstance: this.remoteInstanceAddress })
this.fullscreenLoading = false
},
async reloadEmoji() {
......@@ -206,6 +226,15 @@ export default {
justify-content: space-between;
margin: 0 15px 22px 15px;
}
.emoji-name-warning {
color: #666666;
font-size: 13px;
line-height: 22px;
margin: 5px 0 0 0;
overflow-wrap: break-word;
overflow: hidden;
text-overflow: ellipsis;
}
.emoji-packs-header-button-container {
display: flex;
}
......
......@@ -4,64 +4,70 @@
<h1>{{ $t('mediaProxyCache.mediaProxyCache') }}</h1>
<reboot-button/>
</div>
<p class="media-proxy-cache-header">{{ $t('mediaProxyCache.evictObjectsHeader') }}</p>
<div class="url-input-container">
<el-input
:placeholder="$t('mediaProxyCache.url')"
v-model="urls"
type="textarea"
autosize
clearable
class="url-input"/>
<el-checkbox v-model="ban">{{ $t('mediaProxyCache.ban') }}</el-checkbox>
<el-button class="evict-button" @click="evictURL">{{ $t('mediaProxyCache.evict') }}</el-button>
<div v-if="mediaProxyEnabled">
<p class="media-proxy-cache-header">{{ $t('mediaProxyCache.evictObjectsHeader') }}</p>
<div class="url-input-container">
<el-input
:placeholder="$t('mediaProxyCache.url')"
v-model="urls"
type="textarea"
autosize
clearable
class="url-input"/>
<el-checkbox v-model="ban">{{ $t('mediaProxyCache.ban') }}</el-checkbox>
<el-button class="evict-button" @click="evictURL">{{ $t('mediaProxyCache.evict') }}</el-button>
</div>
<span class="expl url-input-expl">{{ $t('mediaProxyCache.multipleInput') }}</span>
<p class="media-proxy-cache-header">{{ $t('mediaProxyCache.listBannedUrlsHeader') }}</p>
<el-table
v-loading="loading"
:data="bannedUrls"
class="banned-urls-table"
@selection-change="handleSelectionChange">>
<el-table-column
type="selection"
align="center"
width="55"/>
<el-table-column :min-width="isDesktop ? 320 : 120" prop="url">
<template slot="header" slot-scope="scope">
<el-input
:placeholder="$t('users.search')"
v-model="search"
size="mini"
prefix-icon="el-icon-search"
@input="handleDebounceSearchInput"/>
</template>
</el-table-column>
<el-table-column>
<template slot="header">
<el-button
:disabled="removeSelectedDisabled"
size="mini"
class="remove-url-button"
@click="removeSelected()">{{ $t('mediaProxyCache.removeSelected') }}</el-button>
</template>
<template slot-scope="scope">
<el-button
size="mini"
class="remove-url-button"
@click="removeUrl(scope.row.url)">{{ $t('mediaProxyCache.remove') }}</el-button>
</template>
</el-table-column>
</el-table>
<div v-if="!loading" class="pagination">
<el-pagination
:total="urlsCount"
:current-page="currentPage"
:page-size="pageSize"
hide-on-single-page
layout="prev, pager, next"
@current-change="handlePageChange"
/>
</div>
</div>
<span class="expl url-input-expl">{{ $t('mediaProxyCache.multipleInput') }}</span>
<p class="media-proxy-cache-header">{{ $t('mediaProxyCache.listBannedUrlsHeader') }}</p>
<el-table
v-loading="loading"
:data="bannedUrls"
class="banned-urls-table"
@selection-change="handleSelectionChange">>
<el-table-column
type="selection"
align="center"
width="55"/>
<el-table-column :min-width="isDesktop ? 320 : 120" prop="url">
<template slot="header" slot-scope="scope">
<el-input
:placeholder="$t('users.search')"
v-model="search"
size="mini"
prefix-icon="el-icon-search"
@input="handleDebounceSearchInput"/>
</template>
</el-table-column>
<el-table-column>
<template slot="header">
<el-button
:disabled="removeSelectedDisabled"
size="mini"
class="remove-url-button"
@click="removeSelected()">{{ $t('mediaProxyCache.removeSelected') }}</el-button>
</template>
<template slot-scope="scope">
<el-button
size="mini"
class="remove-url-button"
@click="removeUrl(scope.row.url)">{{ $t('mediaProxyCache.remove') }}</el-button>
</template>
</el-table-column>
</el-table>
<div v-if="!loading" class="pagination">
<el-pagination
:total="urlsCount"
:current-page="currentPage"
:page-size="pageSize"
hide-on-single-page
layout="prev, pager, next"
@current-change="handlePageChange"
/>
<div v-else class="enable-mediaproxy-container">
<el-button type="text" @click="enableMediaProxy">{{ $t('mediaProxyCache.enable') }}</el-button>
{{ $t('mediaProxyCache.invalidationAndMediaProxy') }}
</div>
</div>
</template>
......@@ -94,6 +100,9 @@ export default {
loading() {
return this.$store.state.mediaProxyCache.loading
},
mediaProxyEnabled() {
return this.$store.state.mediaProxyCache.mediaProxyEnabled
},
pageSize() {
return this.$store.state.mediaProxyCache.pageSize
},
......@@ -112,11 +121,32 @@ export default {
mounted() {
this.$store.dispatch('GetNodeInfo')
this.$store.dispatch('NeedReboot')
this.$store.dispatch('FetchMediaProxySetting')
this.$store.dispatch('ListBannedUrls', { page: 1 })
},
methods: {
enableMediaProxy() {
this.$confirm(
this.$t('mediaProxyCache.confirmEnablingMediaProxy'),
{
confirmButtonText: 'Yes',
cancelButtonText: 'Cancel',
type: 'warning'
}).then(() => {
this.$message({
type: 'success',
message: this.$t('mediaProxyCache.enableMediaProxySuccessMessage')
})
this.$store.dispatch('EnableMediaProxy')
}).catch(() => {
this.$message({
type: 'info',
message: 'Canceled'
})
})
},
evictURL() {
const urls = this.urls.split(',').map(url => url.trim()).filter(el => el.length > 0)
const urls = this.splitUrls(this.urls)
this.$store.dispatch('PurgeUrls', { urls, ban: this.ban })
this.urls = ''
},
......@@ -133,6 +163,9 @@ export default {
},
removeUrl(url) {
this.$store.dispatch('RemoveBannedUrls', [url])
},
splitUrls(urls) {
return urls.split(',').map(url => url.trim()).filter(el => el.length > 0)
}
}
}
......@@ -142,6 +175,12 @@ export default {
h1 {
margin: 0;
}
.enable-mediaproxy-container {
margin: 10px 15px;
button {
font-size: 16px;
}
}
.expl {
color: #666666;
font-size: 13px;
......
<template>
<el-dropdown trigger="click">
<el-dropdown :hide-on-click="false" trigger="click">
<el-button :disabled="!account.id" plain size="small" icon="el-icon-files">{{ $t('reports.moderateUser') }}
<i class="el-icon-arrow-down el-icon--right"/>
</el-button>
......@@ -11,10 +11,11 @@
</el-dropdown-item>
<el-dropdown-item
v-if="showDeactivatedButton(account.id)"
@click.native="handleDeletion(account.id)">
@click.native="handleDeletion(account)">
{{ $t('users.deleteAccount') }}
</el-dropdown-item>
<el-dropdown-item
v-if="tagPolicyEnabled"
:divided="true"
:class="{ 'active-tag': tags.includes('mrf_tag:media-force-nsfw') }"
@click.native="toggleTag(account, 'mrf_tag:media-force-nsfw')">
......@@ -22,37 +23,47 @@
<i v-if="tags.includes('mrf_tag:media-force-nsfw')" class="el-icon-check"/>
</el-dropdown-item>
<el-dropdown-item
v-if="tagPolicyEnabled"
:class="{ 'active-tag': tags.includes('mrf_tag:media-strip') }"
@click.native="toggleTag(account, 'mrf_tag:media-strip')">
{{ $t('users.stripMedia') }}
<i v-if="tags.includes('mrf_tag:media-strip')" class="el-icon-check"/>
</el-dropdown-item>
<el-dropdown-item
v-if="tagPolicyEnabled"
:class="{ 'active-tag': tags.includes('mrf_tag:force-unlisted') }"
@click.native="toggleTag(account, 'mrf_tag:force-unlisted')">
{{ $t('users.forceUnlisted') }}
<i v-if="tags.includes('mrf_tag:force-unlisted')" class="el-icon-check"/>
</el-dropdown-item>
<el-dropdown-item
v-if="tagPolicyEnabled"
:class="{ 'active-tag': tags.includes('mrf_tag:sandbox') }"
@click.native="toggleTag(account, 'mrf_tag:sandbox')">
{{ $t('users.sandbox') }}
<i v-if="tags.includes('mrf_tag:sandbox')" class="el-icon-check"/>
</el-dropdown-item>
<el-dropdown-item
v-if="account.local"
v-if="tagPolicyEnabled && account.local"
:class="{ 'active-tag': tags.includes('mrf_tag:disable-remote-subscription') }"
@click.native="toggleTag(account, 'mrf_tag:disable-remote-subscription')">
{{ $t('users.disableRemoteSubscription') }}
<i v-if="tags.includes('mrf_tag:disable-remote-subscription')" class="el-icon-check"/>
</el-dropdown-item>
<el-dropdown-item
v-if="account.local"
v-if="tagPolicyEnabled && account.local"
:class="{ 'active-tag': tags.includes('mrf_tag:disable-any-subscription') }"
@click.native="toggleTag(account, 'mrf_tag:disable-any-subscription')">
{{ $t('users.disableAnySubscription') }}
<i v-if="tags.includes('mrf_tag:disable-any-subscription')" class="el-icon-check"/>
</el-dropdown-item>
<el-dropdown-item
v-if="!tagPolicyEnabled"
divided
class="no-hover"
@click.native="enableTagPolicy">
{{ $t('users.enableTagPolicy') }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
......@@ -64,27 +75,69 @@ export default {
account: {
type: Object,
required: true
},
reportId: {
type: String,
required: true
}
},
computed: {
tagPolicyEnabled() {
return this.$store.state.users.mrfPolicies.includes('Pleroma.Web.ActivityPub.MRF.TagPolicy')
},
tags() {
return this.account.tags || []
}
},
methods: {
handleDeactivation({ nickname }) {
this.$store.dispatch('ToggleUserActivation', nickname)
enableTagPolicy() {
this.$confirm(
this.$t('users.confirmEnablingTagPolicy'),
{
confirmButtonText: 'Yes',
cancelButtonText: 'Cancel',
type: 'warning'
}).then(() => {
this.$message({
type: 'success',
message: this.$t('users.enableTagPolicySuccessMessage')
})
this.$store.dispatch('EnableTagPolicy')
}).catch(() => {
this.$message({
type: 'info',
message: 'Canceled'
})
})
},
handleDeactivation(user) {
user.deactivated
? this.$store.dispatch('ActivateUserFromReports', { user, reportId: this.reportId })
: this.$store.dispatch('DeactivateUserFromReports', { user, reportId: this.reportId })
},
handleDeletion(user) {
this.$store.dispatch('DeleteUser', user)
this.$confirm(
this.$t('users.deleteUserConfirmation'),
{
confirmButtonText: 'Delete',
cancelButtonText: 'Cancel',
type: 'warning'
}).then(() => {
this.$store.dispatch('DeleteUserFromReports', { user, reportId: this.reportId })
}).catch(() => {
this.$message({
type: 'info',
message: 'Delete canceled'
})
})
},
showDeactivatedButton(id) {
return this.$store.state.user.id !== id
},
toggleTag(user, tag) {
user.tags.includes(tag)
? this.$store.dispatch('RemoveTag', { users: [user], tag })
: this.$store.dispatch('AddTag', { users: [user], tag })
? this.$store.dispatch('RemoveTagFromReports', { user, tag, reportId: this.reportId })
: this.$store.dispatch('AddTagFromReports', { user, tag, reportId: this.reportId })
}
}
}
......
......@@ -24,7 +24,7 @@
<el-dropdown-item v-if="report.state !== 'closed'" @click.native="changeReportState('closed', report.id)">{{ $t('reports.close') }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<moderate-user-dropdown v-if="propertyExists(report.account, 'nickname')" :account="report.account"/>
<moderate-user-dropdown v-if="propertyExists(report.account, 'nickname')" :account="report.account" :report-id="report.id" />
</div>
</div>
<el-divider class="divider"/>
......
......@@ -41,6 +41,7 @@ export default {
mounted() {
this.$store.dispatch('GetNodeInfo')
this.$store.dispatch('NeedReboot')
this.$store.dispatch('FetchTagPolicySetting')
this.$store.dispatch('FetchReports', 1)
}
}
......
......@@ -15,10 +15,6 @@
<el-form :model="oauth2Data" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="oauth2" :data="oauth2Data"/>
</el-form>
<el-divider v-if="oauth2" class="divider thick-line"/>
<el-form :model="restrictUnauthenticatedData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="restrictUnauthenticated" :data="restrictUnauthenticatedData"/>
</el-form>
<div class="submit-button-container">
<el-button class="submit-button" type="primary" @click="onSubmit">Submit</el-button>
</div>
......@@ -85,12 +81,6 @@ export default {
},
pleromaAuthenticatorData() {
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Web.Auth.Authenticator']) || {}
},
restrictUnauthenticated() {
return this.settings.description.find(setting => setting.key === ':restrict_unauthenticated')
},
restrictUnauthenticatedData() {
return _.get(this.settings.settings, [':pleroma', ':restrict_unauthenticated']) || {}
}
},
methods: {
......
......@@ -42,7 +42,8 @@
@change="update($event, settingGroup.group, settingGroup.key, settingParent, setting.key, setting.type, nested)"
/>
<el-input
v-else-if="setting.type === 'string' || (setting.type.includes('string') && setting.type.includes('atom'))"
v-else-if="setting.type === 'string' ||
(Array.isArray(setting.type) && setting.type.includes('string') && setting.type.includes('atom'))"
:value="inputValue"
:placeholder="setting.suggestions ? setting.suggestions[0] : null"
:data-search="setting.key || setting.group"
......@@ -358,7 +359,10 @@ export default {
)
},
renderSingleSelect(type) {
return !this.reducedSelects && (type === 'module' || (type.includes('atom') && type.includes('dropdown')))
return !this.reducedSelects && (
type === 'module' ||
(Array.isArray(type) && type.includes('atom') && type.includes('dropdown'))
)
},
senderInput({ key, type }) {
return Array.isArray(type) && type.includes('string') && type.includes('tuple') && key === ':sender'
......