Merge branch 'moderation-menu' into 'develop'
Moderation menu #310 See merge request pleroma/pleroma-fe!595
This commit is contained in:
commit
546ced43d9
|
@ -24,12 +24,14 @@
|
||||||
"node-sass": "^3.10.1",
|
"node-sass": "^3.10.1",
|
||||||
"object-path": "^0.11.3",
|
"object-path": "^0.11.3",
|
||||||
"phoenix": "^1.3.0",
|
"phoenix": "^1.3.0",
|
||||||
|
"popper.js": "^1.14.7",
|
||||||
"sanitize-html": "^1.13.0",
|
"sanitize-html": "^1.13.0",
|
||||||
"sass-loader": "^4.0.2",
|
"sass-loader": "^4.0.2",
|
||||||
"vue": "^2.5.13",
|
"vue": "^2.5.13",
|
||||||
"vue-chat-scroll": "^1.2.1",
|
"vue-chat-scroll": "^1.2.1",
|
||||||
"vue-compose": "^0.7.1",
|
"vue-compose": "^0.7.1",
|
||||||
"vue-i18n": "^7.3.2",
|
"vue-i18n": "^7.3.2",
|
||||||
|
"vue-popperjs": "^2.0.3",
|
||||||
"vue-router": "^3.0.1",
|
"vue-router": "^3.0.1",
|
||||||
"vue-template-compiler": "^2.3.4",
|
"vue-template-compiler": "^2.3.4",
|
||||||
"vue-timeago": "^3.1.2",
|
"vue-timeago": "^3.1.2",
|
||||||
|
|
|
@ -101,6 +101,14 @@ button {
|
||||||
background-color: $fallback--bg;
|
background-color: $fallback--bg;
|
||||||
background-color: var(--bg, $fallback--bg)
|
background-color: var(--bg, $fallback--bg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.danger {
|
||||||
|
// TODO: add better color variable
|
||||||
|
color: $fallback--text;
|
||||||
|
color: var(--alertErrorPanelText, $fallback--text);
|
||||||
|
background-color: $fallback--alertError;
|
||||||
|
background-color: var(--alertError, $fallback--alertError);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
label.select {
|
label.select {
|
||||||
|
|
|
@ -211,6 +211,7 @@ const getNodeInfo = async ({ store }) => {
|
||||||
|
|
||||||
const frontendVersion = window.___pleromafe_commit_hash
|
const frontendVersion = window.___pleromafe_commit_hash
|
||||||
store.dispatch('setInstanceOption', { name: 'frontendVersion', value: frontendVersion })
|
store.dispatch('setInstanceOption', { name: 'frontendVersion', value: frontendVersion })
|
||||||
|
store.dispatch('setInstanceOption', { name: 'tagPolicyAvailable', value: metadata.federation.mrf_policies.includes('TagPolicy') })
|
||||||
} else {
|
} else {
|
||||||
throw (res)
|
throw (res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,11 @@ const DeleteButton = {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
currentUser () { return this.$store.state.users.currentUser },
|
currentUser () { return this.$store.state.users.currentUser },
|
||||||
canDelete () { return this.currentUser && this.currentUser.rights.delete_others_notice || this.status.user.id === this.currentUser.id }
|
canDelete () {
|
||||||
|
if (!this.currentUser) { return }
|
||||||
|
const superuser = this.currentUser.rights.moderator || this.currentUser.rights.admin
|
||||||
|
return superuser || this.status.user.id === this.currentUser.id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
const DialogModal = {
|
||||||
|
props: {
|
||||||
|
darkOverlay: {
|
||||||
|
default: true,
|
||||||
|
type: Boolean
|
||||||
|
},
|
||||||
|
onCancel: {
|
||||||
|
default: () => {},
|
||||||
|
type: Function
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DialogModal
|
|
@ -0,0 +1,92 @@
|
||||||
|
<template>
|
||||||
|
<span v-bind:class="{ 'dark-overlay': darkOverlay }" @click.self.stop='onCancel()'>
|
||||||
|
<div class="dialog-modal panel panel-default" @click.stop=''>
|
||||||
|
<div class="panel-heading dialog-modal-heading">
|
||||||
|
<div class="title">
|
||||||
|
<slot name="header"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-modal-content">
|
||||||
|
<slot name="default"></slot>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-modal-footer user-interactions panel-footer">
|
||||||
|
<slot name="footer"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./dialog_modal.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
|
// TODO: unify with other modals.
|
||||||
|
.dark-overlay {
|
||||||
|
&::before {
|
||||||
|
bottom: 0;
|
||||||
|
content: " ";
|
||||||
|
display: block;
|
||||||
|
cursor: default;
|
||||||
|
left: 0;
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
background: rgba(27,31,35,.5);
|
||||||
|
z-index: 99;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-modal.panel {
|
||||||
|
top: 0;
|
||||||
|
left: 50%;
|
||||||
|
max-height: 80vh;
|
||||||
|
max-width: 90vw;
|
||||||
|
margin: 15vh auto;
|
||||||
|
position: fixed;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
z-index: 999;
|
||||||
|
cursor: default;
|
||||||
|
display: block;
|
||||||
|
background-color: $fallback--bg;
|
||||||
|
background-color: var(--bg, $fallback--bg);
|
||||||
|
|
||||||
|
.dialog-modal-heading {
|
||||||
|
padding: .5em .5em;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-bottom: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
color: var(--panelText);
|
||||||
|
background-color: $fallback--fg;
|
||||||
|
background-color: var(--panel, $fallback--fg);
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-modal-content {
|
||||||
|
margin: 0;
|
||||||
|
padding: 1rem 1rem;
|
||||||
|
background-color: $fallback--lightBg;
|
||||||
|
background-color: var(--lightBg, $fallback--lightBg);
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-modal-footer {
|
||||||
|
margin: 0;
|
||||||
|
padding: .5em .5em;
|
||||||
|
background-color: $fallback--lightBg;
|
||||||
|
background-color: var(--lightBg, $fallback--lightBg);
|
||||||
|
border-top: 1px solid $fallback--bg;
|
||||||
|
border-top: 1px solid var(--bg, $fallback--bg);
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: auto;
|
||||||
|
margin-left: .5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,106 @@
|
||||||
|
import DialogModal from '../dialog_modal/dialog_modal.vue'
|
||||||
|
import Popper from 'vue-popperjs/src/component/popper.js.vue'
|
||||||
|
|
||||||
|
const FORCE_NSFW = 'mrf_tag:media-force-nsfw'
|
||||||
|
const STRIP_MEDIA = 'mrf_tag:media-strip'
|
||||||
|
const FORCE_UNLISTED = 'mrf_tag:force-unlisted'
|
||||||
|
const DISABLE_REMOTE_SUBSCRIPTION = 'mrf_tag:disable-remote-subscription'
|
||||||
|
const DISABLE_ANY_SUBSCRIPTION = 'mrf_tag:disable-any-subscription'
|
||||||
|
const SANDBOX = 'mrf_tag:sandbox'
|
||||||
|
const QUARANTINE = 'mrf_tag:quarantine'
|
||||||
|
|
||||||
|
const ModerationTools = {
|
||||||
|
props: [
|
||||||
|
'user'
|
||||||
|
],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
showDropDown: false,
|
||||||
|
tags: {
|
||||||
|
FORCE_NSFW,
|
||||||
|
STRIP_MEDIA,
|
||||||
|
FORCE_UNLISTED,
|
||||||
|
DISABLE_REMOTE_SUBSCRIPTION,
|
||||||
|
DISABLE_ANY_SUBSCRIPTION,
|
||||||
|
SANDBOX,
|
||||||
|
QUARANTINE
|
||||||
|
},
|
||||||
|
showDeleteUserDialog: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
DialogModal,
|
||||||
|
Popper
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
tagsSet () {
|
||||||
|
return new Set(this.user.tags)
|
||||||
|
},
|
||||||
|
hasTagPolicy () {
|
||||||
|
return this.$store.state.instance.tagPolicyAvailable
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleMenu () {
|
||||||
|
this.showDropDown = !this.showDropDown
|
||||||
|
},
|
||||||
|
hasTag (tagName) {
|
||||||
|
return this.tagsSet.has(tagName)
|
||||||
|
},
|
||||||
|
toggleTag (tag) {
|
||||||
|
const store = this.$store
|
||||||
|
if (this.tagsSet.has(tag)) {
|
||||||
|
store.state.api.backendInteractor.untagUser(this.user, tag).then(response => {
|
||||||
|
if (!response.ok) { return }
|
||||||
|
store.commit('untagUser', {user: this.user, tag})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
store.state.api.backendInteractor.tagUser(this.user, tag).then(response => {
|
||||||
|
if (!response.ok) { return }
|
||||||
|
store.commit('tagUser', {user: this.user, tag})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toggleRight (right) {
|
||||||
|
const store = this.$store
|
||||||
|
if (this.user.rights[right]) {
|
||||||
|
store.state.api.backendInteractor.deleteRight(this.user, right).then(response => {
|
||||||
|
if (!response.ok) { return }
|
||||||
|
store.commit('updateRight', {user: this.user, right: right, value: false})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
store.state.api.backendInteractor.addRight(this.user, right).then(response => {
|
||||||
|
if (!response.ok) { return }
|
||||||
|
store.commit('updateRight', {user: this.user, right: right, value: true})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toggleActivationStatus () {
|
||||||
|
const store = this.$store
|
||||||
|
const status = !!this.user.deactivated
|
||||||
|
store.state.api.backendInteractor.setActivationStatus(this.user, status).then(response => {
|
||||||
|
if (!response.ok) { return }
|
||||||
|
store.commit('updateActivationStatus', {user: this.user, status: status})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
deleteUserDialog (show) {
|
||||||
|
this.showDeleteUserDialog = show
|
||||||
|
},
|
||||||
|
deleteUser () {
|
||||||
|
const store = this.$store
|
||||||
|
const user = this.user
|
||||||
|
const {id, name} = user
|
||||||
|
store.state.api.backendInteractor.deleteUser(user)
|
||||||
|
.then(e => {
|
||||||
|
this.$store.dispatch('markStatusesAsDeleted', status => user.id === status.user.id)
|
||||||
|
const isProfile = this.$route.name === 'external-user-profile' || this.$route.name === 'user-profile'
|
||||||
|
const isTargetUser = this.$route.params.name === name || this.$route.params.id === id
|
||||||
|
if (isProfile && isTargetUser) {
|
||||||
|
window.history.back()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModerationTools
|
|
@ -0,0 +1,158 @@
|
||||||
|
<template>
|
||||||
|
<div class='block' style='position: relative'>
|
||||||
|
<Popper
|
||||||
|
trigger="click"
|
||||||
|
@hide='showDropDown = false'
|
||||||
|
append-to-body
|
||||||
|
:options="{
|
||||||
|
placement: 'bottom-end',
|
||||||
|
modifiers: {
|
||||||
|
arrow: { enabled: true },
|
||||||
|
offset: { offset: '0, 5px' },
|
||||||
|
}
|
||||||
|
}">
|
||||||
|
<div class="popper-wrapper">
|
||||||
|
<div class="dropdown-menu">
|
||||||
|
<span v-if='user.is_local'>
|
||||||
|
<button class="dropdown-item" @click='toggleRight("admin")'>
|
||||||
|
{{ $t(!!user.rights.admin ? 'user_card.admin_menu.revoke_admin' : 'user_card.admin_menu.grant_admin') }}
|
||||||
|
</button>
|
||||||
|
<button class="dropdown-item" @click='toggleRight("moderator")'>
|
||||||
|
{{ $t(!!user.rights.moderator ? 'user_card.admin_menu.revoke_moderator' : 'user_card.admin_menu.grant_moderator') }}
|
||||||
|
</button>
|
||||||
|
<div role="separator" class="dropdown-divider"></div>
|
||||||
|
</span>
|
||||||
|
<button class="dropdown-item" @click='toggleActivationStatus()'>
|
||||||
|
{{ $t(!!user.deactivated ? 'user_card.admin_menu.activate_account' : 'user_card.admin_menu.deactivate_account') }}
|
||||||
|
</button>
|
||||||
|
<button class="dropdown-item" @click='deleteUserDialog(true)'>
|
||||||
|
{{ $t('user_card.admin_menu.delete_account') }}
|
||||||
|
</button>
|
||||||
|
<div role="separator" class="dropdown-divider" v-if='hasTagPolicy'></div>
|
||||||
|
<span v-if='hasTagPolicy'>
|
||||||
|
<button class="dropdown-item" @click='toggleTag(tags.FORCE_NSFW)'>
|
||||||
|
{{ $t('user_card.admin_menu.force_nsfw') }}
|
||||||
|
<span class="menu-checkbox" v-bind:class="{ 'menu-checkbox-checked': hasTag(tags.FORCE_NSFW) }"></span>
|
||||||
|
</button>
|
||||||
|
<button class="dropdown-item" @click='toggleTag(tags.STRIP_MEDIA)'>
|
||||||
|
{{ $t('user_card.admin_menu.strip_media') }}
|
||||||
|
<span class="menu-checkbox" v-bind:class="{ 'menu-checkbox-checked': hasTag(tags.STRIP_MEDIA) }"></span>
|
||||||
|
</button>
|
||||||
|
<button class="dropdown-item" @click='toggleTag(tags.FORCE_UNLISTED)'>
|
||||||
|
{{ $t('user_card.admin_menu.force_unlisted') }}
|
||||||
|
<span class="menu-checkbox" v-bind:class="{ 'menu-checkbox-checked': hasTag(tags.FORCE_UNLISTED) }"></span>
|
||||||
|
</button>
|
||||||
|
<button class="dropdown-item" @click='toggleTag(tags.SANDBOX)'>
|
||||||
|
{{ $t('user_card.admin_menu.sandbox') }}
|
||||||
|
<span class="menu-checkbox" v-bind:class="{ 'menu-checkbox-checked': hasTag(tags.SANDBOX) }"></span>
|
||||||
|
</button>
|
||||||
|
<button class="dropdown-item" v-if='user.is_local' @click='toggleTag(tags.DISABLE_REMOTE_SUBSCRIPTION)'>
|
||||||
|
{{ $t('user_card.admin_menu.disable_remote_subscription') }}
|
||||||
|
<span class="menu-checkbox" v-bind:class="{ 'menu-checkbox-checked': hasTag(tags.DISABLE_REMOTE_SUBSCRIPTION) }"></span>
|
||||||
|
</button>
|
||||||
|
<button class="dropdown-item" v-if='user.is_local' @click='toggleTag(tags.DISABLE_ANY_SUBSCRIPTION)'>
|
||||||
|
{{ $t('user_card.admin_menu.disable_any_subscription') }}
|
||||||
|
<span class="menu-checkbox" v-bind:class="{ 'menu-checkbox-checked': hasTag(tags.DISABLE_ANY_SUBSCRIPTION) }"></span>
|
||||||
|
</button>
|
||||||
|
<button class="dropdown-item" v-if='user.is_local' @click='toggleTag(tags.QUARANTINE)'>
|
||||||
|
{{ $t('user_card.admin_menu.quarantine') }}
|
||||||
|
<span class="menu-checkbox" v-bind:class="{ 'menu-checkbox-checked': hasTag(tags.QUARANTINE) }"></span>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button slot="reference" v-bind:class="{ pressed: showDropDown }" @click='toggleMenu'>
|
||||||
|
{{ $t('user_card.admin_menu.moderation') }}
|
||||||
|
</button>
|
||||||
|
</Popper>
|
||||||
|
<DialogModal v-if="showDeleteUserDialog" :onCancel='deleteUserDialog.bind(this, false)'>
|
||||||
|
<span slot="header">{{ $t('user_card.admin_menu.delete_user') }}</span>
|
||||||
|
<p>{{ $t('user_card.admin_menu.delete_user_confirmation') }}</p>
|
||||||
|
<span slot="footer">
|
||||||
|
<button @click='deleteUserDialog(false)'>
|
||||||
|
{{ $t('general.cancel') }}
|
||||||
|
</button>
|
||||||
|
<button class="danger" @click='deleteUser()'>
|
||||||
|
{{ $t('user_card.admin_menu.delete_user') }}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</DialogModal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./moderation_tools.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '../../_variables.scss';
|
||||||
|
@import '../popper/popper.scss';
|
||||||
|
|
||||||
|
.dropdown-menu {
|
||||||
|
display: block;
|
||||||
|
padding: .5rem 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
text-align: left;
|
||||||
|
list-style: none;
|
||||||
|
max-width: 100vw;
|
||||||
|
z-index: 10;
|
||||||
|
box-shadow: 1px 1px 4px rgba(0,0,0,.6);
|
||||||
|
box-shadow: var(--panelShadow);
|
||||||
|
border: none;
|
||||||
|
border-radius: $fallback--btnRadius;
|
||||||
|
border-radius: var(--btnRadius, $fallback--btnRadius);
|
||||||
|
background-color: $fallback--bg;
|
||||||
|
background-color: var(--bg, $fallback--bg);
|
||||||
|
|
||||||
|
.dropdown-divider {
|
||||||
|
height: 0;
|
||||||
|
margin: .5rem 0;
|
||||||
|
overflow: hidden;
|
||||||
|
border-top: 1px solid $fallback--border;
|
||||||
|
border-top: 1px solid var(--border, $fallback--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item {
|
||||||
|
line-height: 21px;
|
||||||
|
margin-right: 5px;
|
||||||
|
overflow: auto;
|
||||||
|
display: block;
|
||||||
|
padding: .25rem 1.0rem .25rem 1.5rem;
|
||||||
|
clear: both;
|
||||||
|
font-weight: 400;
|
||||||
|
text-align: inherit;
|
||||||
|
white-space: normal;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0px;
|
||||||
|
background-color: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
// TODO: improve the look on breeze themes
|
||||||
|
background-color: $fallback--fg;
|
||||||
|
background-color: var(--btn, $fallback--fg);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-checkbox {
|
||||||
|
float: right;
|
||||||
|
min-width: 22px;
|
||||||
|
max-width: 22px;
|
||||||
|
min-height: 22px;
|
||||||
|
max-height: 22px;
|
||||||
|
line-height: 22px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 0px;
|
||||||
|
background-color: $fallback--fg;
|
||||||
|
background-color: var(--input, $fallback--fg);
|
||||||
|
box-shadow: 0px 0px 2px black inset;
|
||||||
|
box-shadow: var(--inputShadow);
|
||||||
|
|
||||||
|
&.menu-checkbox-checked::after {
|
||||||
|
content: '✔';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -21,6 +21,9 @@ const Notification = {
|
||||||
},
|
},
|
||||||
userProfileLink (user) {
|
userProfileLink (user) {
|
||||||
return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames)
|
return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames)
|
||||||
|
},
|
||||||
|
getUser (notification) {
|
||||||
|
return this.$store.state.users.usersObject[notification.action.user.id]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<UserAvatar :compact="true" :betterShadow="betterShadow" :src="notification.action.user.profile_image_url_original"/>
|
<UserAvatar :compact="true" :betterShadow="betterShadow" :src="notification.action.user.profile_image_url_original"/>
|
||||||
</a>
|
</a>
|
||||||
<div class='notification-right'>
|
<div class='notification-right'>
|
||||||
<UserCard :user="user" :rounded="true" :bordered="true" v-if="userExpanded"/>
|
<UserCard :user="getUser(notification)" :rounded="true" :bordered="true" v-if="userExpanded"/>
|
||||||
<span class="notification-details">
|
<span class="notification-details">
|
||||||
<div class="name-and-action">
|
<div class="name-and-action">
|
||||||
<span class="username" v-if="!!notification.action.user.name_html" :title="'@'+notification.action.user.screen_name" v-html="notification.action.user.name_html"></span>
|
<span class="username" v-if="!!notification.action.user.name_html" :title="'@'+notification.action.user.screen_name" v-html="notification.action.user.name_html"></span>
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
|
.popper-wrapper {
|
||||||
|
z-index: 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popper-wrapper .popper__arrow {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-style: solid;
|
||||||
|
position: absolute;
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popper-wrapper[x-placement^="top"] {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popper-wrapper[x-placement^="top"] .popper__arrow {
|
||||||
|
border-width: 5px 5px 0 5px;
|
||||||
|
border-color: $fallback--bg transparent transparent transparent;
|
||||||
|
border-color: var(--bg, $fallback--bg) transparent transparent transparent;
|
||||||
|
bottom: -5px;
|
||||||
|
left: calc(50% - 5px);
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popper-wrapper[x-placement^="bottom"] {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popper-wrapper[x-placement^="bottom"] .popper__arrow {
|
||||||
|
border-width: 0 5px 5px 5px;
|
||||||
|
border-color: transparent transparent $fallback--bg transparent;
|
||||||
|
border-color: transparent transparent var(--bg, $fallback--bg) transparent;
|
||||||
|
top: -5px;
|
||||||
|
left: calc(50% - 5px);
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popper-wrapper[x-placement^="right"] {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popper-wrapper[x-placement^="right"] .popper__arrow {
|
||||||
|
border-width: 5px 5px 5px 0;
|
||||||
|
border-color: transparent $fallback--bg transparent transparent;
|
||||||
|
border-color: transparent var(--bg, $fallback--bg) transparent transparent;
|
||||||
|
left: -5px;
|
||||||
|
top: calc(50% - 5px);
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popper-wrapper[x-placement^="left"] {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popper-wrapper[x-placement^="left"] .popper__arrow {
|
||||||
|
border-width: 5px 0 5px 5px;
|
||||||
|
border-color: transparent transparent transparent $fallback--bg;
|
||||||
|
border-color: transparent transparent transparent var(--bg, $fallback--bg);
|
||||||
|
right: -5px;
|
||||||
|
top: calc(50% - 5px);
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import UserAvatar from '../user_avatar/user_avatar.vue'
|
import UserAvatar from '../user_avatar/user_avatar.vue'
|
||||||
import RemoteFollow from '../remote_follow/remote_follow.vue'
|
import RemoteFollow from '../remote_follow/remote_follow.vue'
|
||||||
|
import ModerationTools from '../moderation_tools/moderation_tools.vue'
|
||||||
import { hex2rgb } from '../../services/color_convert/color_convert.js'
|
import { hex2rgb } from '../../services/color_convert/color_convert.js'
|
||||||
import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
|
import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
|
||||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||||
|
@ -93,15 +94,17 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
visibleRole () {
|
visibleRole () {
|
||||||
const validRole = (this.user.role === 'admin' || this.user.role === 'moderator')
|
const rights = this.user.rights
|
||||||
const showRole = this.isOtherUser || this.user.show_role
|
if (!rights) { return }
|
||||||
|
const validRole = rights.admin || rights.moderator
|
||||||
return validRole && showRole && this.user.role
|
const roleTitle = rights.admin ? 'admin' : 'moderator'
|
||||||
|
return validRole && roleTitle
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
UserAvatar,
|
UserAvatar,
|
||||||
RemoteFollow
|
RemoteFollow,
|
||||||
|
ModerationTools
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
followUser () {
|
followUser () {
|
||||||
|
|
|
@ -99,6 +99,8 @@
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<ModerationTools :user='user' v-if='loggedIn.role === "admin"'>
|
||||||
|
</ModerationTools>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,6 +3,7 @@ import get from 'lodash/get'
|
||||||
import UserCard from '../user_card/user_card.vue'
|
import UserCard from '../user_card/user_card.vue'
|
||||||
import FollowCard from '../follow_card/follow_card.vue'
|
import FollowCard from '../follow_card/follow_card.vue'
|
||||||
import Timeline from '../timeline/timeline.vue'
|
import Timeline from '../timeline/timeline.vue'
|
||||||
|
import ModerationTools from '../moderation_tools/moderation_tools.vue'
|
||||||
import withLoadMore from '../../hocs/with_load_more/with_load_more'
|
import withLoadMore from '../../hocs/with_load_more/with_load_more'
|
||||||
import withList from '../../hocs/with_list/with_list'
|
import withList from '../../hocs/with_list/with_list'
|
||||||
|
|
||||||
|
@ -155,7 +156,8 @@ const UserProfile = {
|
||||||
UserCard,
|
UserCard,
|
||||||
Timeline,
|
Timeline,
|
||||||
FollowerList,
|
FollowerList,
|
||||||
FriendList
|
FriendList,
|
||||||
|
ModerationTools
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,8 @@
|
||||||
"generic_error": "An error occured",
|
"generic_error": "An error occured",
|
||||||
"optional": "optional",
|
"optional": "optional",
|
||||||
"show_more": "Show more",
|
"show_more": "Show more",
|
||||||
"show_less": "Show less"
|
"show_less": "Show less",
|
||||||
|
"cancel": "Cancel"
|
||||||
},
|
},
|
||||||
"image_cropper": {
|
"image_cropper": {
|
||||||
"crop_picture": "Crop picture",
|
"crop_picture": "Crop picture",
|
||||||
|
@ -403,7 +404,26 @@
|
||||||
"block_progress": "Blocking...",
|
"block_progress": "Blocking...",
|
||||||
"unmute": "Unmute",
|
"unmute": "Unmute",
|
||||||
"unmute_progress": "Unmuting...",
|
"unmute_progress": "Unmuting...",
|
||||||
"mute_progress": "Muting..."
|
"mute_progress": "Muting...",
|
||||||
|
"admin_menu": {
|
||||||
|
"moderation": "Moderation",
|
||||||
|
"grant_admin": "Grant Admin",
|
||||||
|
"revoke_admin": "Revoke Admin",
|
||||||
|
"grant_moderator": "Grant Moderator",
|
||||||
|
"revoke_moderator": "Revoke Moderator",
|
||||||
|
"activate_account": "Activate account",
|
||||||
|
"deactivate_account": "Deactivate account",
|
||||||
|
"delete_account": "Delete account",
|
||||||
|
"force_nsfw": "Mark all posts as NSFW",
|
||||||
|
"strip_media": "Remove media from posts",
|
||||||
|
"force_unlisted": "Force posts to be unlisted",
|
||||||
|
"sandbox": "Force posts to be followers-only",
|
||||||
|
"disable_remote_subscription": "Disallow following user from remote instances",
|
||||||
|
"disable_any_subscription": "Disallow following user at all",
|
||||||
|
"quarantine": "Disallow user posts from federating",
|
||||||
|
"delete_user": "Delete user",
|
||||||
|
"delete_user_confirmation": "Are you absolutely sure? This action cannot be undone."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"user_profile": {
|
"user_profile": {
|
||||||
"timeline_title": "User Timeline",
|
"timeline_title": "User Timeline",
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"apply": "Применить",
|
"apply": "Применить",
|
||||||
"submit": "Отправить"
|
"submit": "Отправить",
|
||||||
|
"cancel": "Отмена"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"login": "Войти",
|
"login": "Войти",
|
||||||
|
@ -311,7 +312,26 @@
|
||||||
"muted": "Игнорирую",
|
"muted": "Игнорирую",
|
||||||
"per_day": "в день",
|
"per_day": "в день",
|
||||||
"remote_follow": "Читать удалённо",
|
"remote_follow": "Читать удалённо",
|
||||||
"statuses": "Статусы"
|
"statuses": "Статусы",
|
||||||
|
"admin_menu": {
|
||||||
|
"moderation": "Опции модератора",
|
||||||
|
"grant_admin": "Сделать администратором",
|
||||||
|
"revoke_admin": "Забрать права администратора",
|
||||||
|
"grant_moderator": "Сделать модератором",
|
||||||
|
"revoke_moderator": "Забрать права модератора",
|
||||||
|
"activate_account": "Активировать аккаунт",
|
||||||
|
"deactivate_account": "Деактивировать аккаунт",
|
||||||
|
"delete_account": "Удалить аккаунт",
|
||||||
|
"force_nsfw": "Отмечать посты пользователя как NSFW",
|
||||||
|
"strip_media": "Убирать вложения из постов пользователя",
|
||||||
|
"force_unlisted": "Не добавлять посты в публичные ленты",
|
||||||
|
"sandbox": "Посты доступны только для подписчиков",
|
||||||
|
"disable_remote_subscription": "Запретить подписываться с удаленных серверов",
|
||||||
|
"disable_any_subscription": "Запретить подписываться на пользователя",
|
||||||
|
"quarantine": "Не федерировать посты пользователя",
|
||||||
|
"delete_user": "Удалить пользователя",
|
||||||
|
"delete_user_confirmation": "Вы уверены? Это действие нельзя отменить."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"user_profile": {
|
"user_profile": {
|
||||||
"timeline_title": "Лента пользователя"
|
"timeline_title": "Лента пользователя"
|
||||||
|
|
|
@ -378,6 +378,13 @@ export const mutations = {
|
||||||
const newStatus = state.allStatusesObject[status.id]
|
const newStatus = state.allStatusesObject[status.id]
|
||||||
newStatus.deleted = true
|
newStatus.deleted = true
|
||||||
},
|
},
|
||||||
|
setManyDeleted (state, condition) {
|
||||||
|
Object.values(state.allStatusesObject).forEach(status => {
|
||||||
|
if (condition(status)) {
|
||||||
|
status.deleted = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
setLoading (state, { timeline, value }) {
|
setLoading (state, { timeline, value }) {
|
||||||
state.timelines[timeline].loading = value
|
state.timelines[timeline].loading = value
|
||||||
},
|
},
|
||||||
|
@ -438,6 +445,9 @@ const statuses = {
|
||||||
commit('setDeleted', { status })
|
commit('setDeleted', { status })
|
||||||
apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })
|
apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })
|
||||||
},
|
},
|
||||||
|
markStatusesAsDeleted ({ commit }, condition) {
|
||||||
|
commit('setManyDeleted', condition)
|
||||||
|
},
|
||||||
favorite ({ rootState, commit }, status) {
|
favorite ({ rootState, commit }, status) {
|
||||||
// Optimistic favoriting...
|
// Optimistic favoriting...
|
||||||
commit('setFavorited', { status, value: true })
|
commit('setFavorited', { status, value: true })
|
||||||
|
|
|
@ -37,6 +37,28 @@ export const mutations = {
|
||||||
const user = state.usersObject[id]
|
const user = state.usersObject[id]
|
||||||
set(user, 'muted', muted)
|
set(user, 'muted', muted)
|
||||||
},
|
},
|
||||||
|
tagUser (state, { user: { id }, tag }) {
|
||||||
|
const user = state.usersObject[id]
|
||||||
|
const tags = user.tags || []
|
||||||
|
const newTags = tags.concat([tag])
|
||||||
|
set(user, 'tags', newTags)
|
||||||
|
},
|
||||||
|
untagUser (state, { user: { id }, tag }) {
|
||||||
|
const user = state.usersObject[id]
|
||||||
|
const tags = user.tags || []
|
||||||
|
const newTags = tags.filter(t => t !== tag)
|
||||||
|
set(user, 'tags', newTags)
|
||||||
|
},
|
||||||
|
updateRight (state, { user: { id }, right, value }) {
|
||||||
|
const user = state.usersObject[id]
|
||||||
|
let newRights = user.rights
|
||||||
|
newRights[right] = value
|
||||||
|
set(user, 'rights', newRights)
|
||||||
|
},
|
||||||
|
updateActivationStatus (state, { user: { id }, status }) {
|
||||||
|
const user = state.usersObject[id]
|
||||||
|
set(user, 'deactivated', !status)
|
||||||
|
},
|
||||||
setCurrentUser (state, user) {
|
setCurrentUser (state, user) {
|
||||||
state.lastLoginName = user.screen_name
|
state.lastLoginName = user.screen_name
|
||||||
state.currentUser = merge(state.currentUser || {}, user)
|
state.currentUser = merge(state.currentUser || {}, user)
|
||||||
|
|
|
@ -16,6 +16,10 @@ const CHANGE_PASSWORD_URL = '/api/pleroma/change_password'
|
||||||
const FOLLOW_REQUESTS_URL = '/api/pleroma/friend_requests'
|
const FOLLOW_REQUESTS_URL = '/api/pleroma/friend_requests'
|
||||||
const APPROVE_USER_URL = '/api/pleroma/friendships/approve'
|
const APPROVE_USER_URL = '/api/pleroma/friendships/approve'
|
||||||
const DENY_USER_URL = '/api/pleroma/friendships/deny'
|
const DENY_USER_URL = '/api/pleroma/friendships/deny'
|
||||||
|
const TAG_USER_URL = '/api/pleroma/admin/users/tag'
|
||||||
|
const PERMISSION_GROUP_URL = '/api/pleroma/admin/permission_group'
|
||||||
|
const ACTIVATION_STATUS_URL = '/api/pleroma/admin/activation_status'
|
||||||
|
const ADMIN_USER_URL = '/api/pleroma/admin/user'
|
||||||
const SUGGESTIONS_URL = '/api/v1/suggestions'
|
const SUGGESTIONS_URL = '/api/v1/suggestions'
|
||||||
|
|
||||||
const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites'
|
const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites'
|
||||||
|
@ -352,6 +356,86 @@ const fetchStatus = ({id, credentials}) => {
|
||||||
.then((data) => parseStatus(data))
|
.then((data) => parseStatus(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tagUser = ({tag, credentials, ...options}) => {
|
||||||
|
const screenName = options.screen_name
|
||||||
|
const form = {
|
||||||
|
nicknames: [screenName],
|
||||||
|
tags: [tag]
|
||||||
|
}
|
||||||
|
|
||||||
|
const headers = authHeaders(credentials)
|
||||||
|
headers['Content-Type'] = 'application/json'
|
||||||
|
|
||||||
|
return fetch(TAG_USER_URL, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: headers,
|
||||||
|
body: JSON.stringify(form)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const untagUser = ({tag, credentials, ...options}) => {
|
||||||
|
const screenName = options.screen_name
|
||||||
|
const body = {
|
||||||
|
nicknames: [screenName],
|
||||||
|
tags: [tag]
|
||||||
|
}
|
||||||
|
|
||||||
|
const headers = authHeaders(credentials)
|
||||||
|
headers['Content-Type'] = 'application/json'
|
||||||
|
|
||||||
|
return fetch(TAG_USER_URL, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: headers,
|
||||||
|
body: JSON.stringify(body)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const addRight = ({right, credentials, ...user}) => {
|
||||||
|
const screenName = user.screen_name
|
||||||
|
|
||||||
|
return fetch(`${PERMISSION_GROUP_URL}/${screenName}/${right}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: authHeaders(credentials),
|
||||||
|
body: {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteRight = ({right, credentials, ...user}) => {
|
||||||
|
const screenName = user.screen_name
|
||||||
|
|
||||||
|
return fetch(`${PERMISSION_GROUP_URL}/${screenName}/${right}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: authHeaders(credentials),
|
||||||
|
body: {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const setActivationStatus = ({status, credentials, ...user}) => {
|
||||||
|
const screenName = user.screen_name
|
||||||
|
const body = {
|
||||||
|
status: status
|
||||||
|
}
|
||||||
|
|
||||||
|
const headers = authHeaders(credentials)
|
||||||
|
headers['Content-Type'] = 'application/json'
|
||||||
|
|
||||||
|
return fetch(`${ACTIVATION_STATUS_URL}/${screenName}.json`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: headers,
|
||||||
|
body: JSON.stringify(body)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteUser = ({credentials, ...user}) => {
|
||||||
|
const screenName = user.screen_name
|
||||||
|
const headers = authHeaders(credentials)
|
||||||
|
|
||||||
|
return fetch(`${ADMIN_USER_URL}.json?nickname=${screenName}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: headers
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const fetchTimeline = ({timeline, credentials, since = false, until = false, userId = false, tag = false, withMuted = false}) => {
|
const fetchTimeline = ({timeline, credentials, since = false, until = false, userId = false, tag = false, withMuted = false}) => {
|
||||||
const timelineUrls = {
|
const timelineUrls = {
|
||||||
public: MASTODON_PUBLIC_TIMELINE,
|
public: MASTODON_PUBLIC_TIMELINE,
|
||||||
|
@ -666,6 +750,12 @@ const apiService = {
|
||||||
fetchBlocks,
|
fetchBlocks,
|
||||||
fetchOAuthTokens,
|
fetchOAuthTokens,
|
||||||
revokeOAuthToken,
|
revokeOAuthToken,
|
||||||
|
tagUser,
|
||||||
|
untagUser,
|
||||||
|
deleteUser,
|
||||||
|
addRight,
|
||||||
|
deleteRight,
|
||||||
|
setActivationStatus,
|
||||||
register,
|
register,
|
||||||
getCaptcha,
|
getCaptcha,
|
||||||
updateAvatar,
|
updateAvatar,
|
||||||
|
|
|
@ -62,6 +62,30 @@ const backendInteractorService = (credentials) => {
|
||||||
return timelineFetcherService.startFetching({timeline, store, credentials, userId, tag})
|
return timelineFetcherService.startFetching({timeline, store, credentials, userId, tag})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tagUser = ({screen_name}, tag) => {
|
||||||
|
return apiService.tagUser({screen_name, tag, credentials})
|
||||||
|
}
|
||||||
|
|
||||||
|
const untagUser = ({screen_name}, tag) => {
|
||||||
|
return apiService.untagUser({screen_name, tag, credentials})
|
||||||
|
}
|
||||||
|
|
||||||
|
const addRight = ({screen_name}, right) => {
|
||||||
|
return apiService.addRight({screen_name, right, credentials})
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteRight = ({screen_name}, right) => {
|
||||||
|
return apiService.deleteRight({screen_name, right, credentials})
|
||||||
|
}
|
||||||
|
|
||||||
|
const setActivationStatus = ({screen_name}, status) => {
|
||||||
|
return apiService.setActivationStatus({screen_name, status, credentials})
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteUser = ({screen_name}) => {
|
||||||
|
return apiService.deleteUser({screen_name, credentials})
|
||||||
|
}
|
||||||
|
|
||||||
const fetchMutes = () => apiService.fetchMutes({credentials})
|
const fetchMutes = () => apiService.fetchMutes({credentials})
|
||||||
const muteUser = (id) => apiService.muteUser({credentials, id})
|
const muteUser = (id) => apiService.muteUser({credentials, id})
|
||||||
const unmuteUser = (id) => apiService.unmuteUser({credentials, id})
|
const unmuteUser = (id) => apiService.unmuteUser({credentials, id})
|
||||||
|
@ -104,6 +128,12 @@ const backendInteractorService = (credentials) => {
|
||||||
fetchBlocks,
|
fetchBlocks,
|
||||||
fetchOAuthTokens,
|
fetchOAuthTokens,
|
||||||
revokeOAuthToken,
|
revokeOAuthToken,
|
||||||
|
tagUser,
|
||||||
|
untagUser,
|
||||||
|
addRight,
|
||||||
|
deleteRight,
|
||||||
|
deleteUser,
|
||||||
|
setActivationStatus,
|
||||||
register,
|
register,
|
||||||
getCaptcha,
|
getCaptcha,
|
||||||
updateAvatar,
|
updateAvatar,
|
||||||
|
|
|
@ -67,6 +67,11 @@ export const parseUser = (data) => {
|
||||||
output.statusnet_blocking = relationship.blocking
|
output.statusnet_blocking = relationship.blocking
|
||||||
output.muted = relationship.muting
|
output.muted = relationship.muting
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output.rights = {
|
||||||
|
moderator: data.pleroma.is_moderator,
|
||||||
|
admin: data.pleroma.is_admin
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Missing, trying to recover
|
// Missing, trying to recover
|
||||||
|
@ -103,7 +108,12 @@ export const parseUser = (data) => {
|
||||||
|
|
||||||
// QVITTER ONLY FOR NOW
|
// QVITTER ONLY FOR NOW
|
||||||
// Really only applies to logged in user, really.. I THINK
|
// Really only applies to logged in user, really.. I THINK
|
||||||
output.rights = data.rights
|
if (data.rights) {
|
||||||
|
output.rights = {
|
||||||
|
moderator: data.rights.delete_others_notice,
|
||||||
|
admin: data.rights.admin
|
||||||
|
}
|
||||||
|
}
|
||||||
output.no_rich_text = data.no_rich_text
|
output.no_rich_text = data.no_rich_text
|
||||||
output.default_scope = data.default_scope
|
output.default_scope = data.default_scope
|
||||||
output.hide_follows = data.hide_follows
|
output.hide_follows = data.hide_follows
|
||||||
|
@ -125,6 +135,13 @@ export const parseUser = (data) => {
|
||||||
output.follow_request_count = data.pleroma.follow_request_count
|
output.follow_request_count = data.pleroma.follow_request_count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.pleroma) {
|
||||||
|
output.tags = data.pleroma.tags
|
||||||
|
output.deactivated = data.pleroma.deactivated
|
||||||
|
}
|
||||||
|
|
||||||
|
output.tags = output.tags || []
|
||||||
|
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -4831,6 +4831,10 @@ pluralize@^1.2.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45"
|
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45"
|
||||||
|
|
||||||
|
popper.js@^1.14.7:
|
||||||
|
version "1.14.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.14.7.tgz#e31ec06cfac6a97a53280c3e55e4e0c860e7738e"
|
||||||
|
|
||||||
posix-character-classes@^0.1.0:
|
posix-character-classes@^0.1.0:
|
||||||
version "0.1.1"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
|
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
|
||||||
|
@ -6457,6 +6461,12 @@ vue-loader@^11.1.0:
|
||||||
vue-style-loader "^2.0.0"
|
vue-style-loader "^2.0.0"
|
||||||
vue-template-es2015-compiler "^1.2.2"
|
vue-template-es2015-compiler "^1.2.2"
|
||||||
|
|
||||||
|
vue-popperjs@^2.0.3:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue-popperjs/-/vue-popperjs-2.0.3.tgz#7c446d0ba7c63170ccb33a02669d0df4efc3d8cd"
|
||||||
|
dependencies:
|
||||||
|
popper.js "^1.14.7"
|
||||||
|
|
||||||
vue-router@^3.0.1:
|
vue-router@^3.0.1:
|
||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.2.tgz#dedc67afe6c4e2bc25682c8b1c2a8c0d7c7e56be"
|
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.2.tgz#dedc67afe6c4e2bc25682c8b1c2a8c0d7c7e56be"
|
||||||
|
|
Loading…
Reference in New Issue