parent
ac28e8c2f9
commit
6b6878bde0
@ -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>
|
@ -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;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in new issue