fix: Add option to transfer anon event ownership, fix #394

This commit is contained in:
lesion 2024-05-16 23:34:41 +02:00
parent bbad9d127d
commit 7b748bd49c
No known key found for this signature in database
GPG key ID: 352918250B012177
7 changed files with 94 additions and 5 deletions

View file

@ -48,6 +48,12 @@ span
v-list-item-content v-list-item-content
v-list-item-title(v-text="$t('event.disable_author')") v-list-item-title(v-text="$t('event.disable_author')")
v-list-item(v-if='event.isAnon && $auth?.user?.is_admin' @click='$emit("openAssignAuthor")')
v-list-item-icon
v-icon(v-text='mdiClipboardAccount')
v-list-item-content
v-list-item-title(v-text="$t('event.assign_to_user')")
template(v-if='event.parentId') template(v-if='event.parentId')
v-list-item.text-overline(v-html="$t('common.recurring_event_actions')") v-list-item.text-overline(v-html="$t('common.recurring_event_actions')")
@ -58,7 +64,6 @@ span
v-list-item-content v-list-item-content
v-list-item-title(v-text="$t(`common.${event.parent.is_visible ? 'pause': 'start'}`)") v-list-item-title(v-text="$t(`common.${event.parent.is_visible ? 'pause': 'start'}`)")
//- Edit event //- Edit event
v-list-item(:to='`/add/${event.parentId}`') v-list-item(:to='`/add/${event.parentId}`')
v-list-item-icon v-list-item-icon
@ -76,13 +81,15 @@ span
</template> </template>
<script> <script>
import { mdiChevronUp, mdiRepeat, mdiDelete, mdiCalendarEdit, mdiEyeOff, mdiEye, mdiPause, mdiPlay, mdiDeleteForever, mdiScanner, mdiMessageTextOutline, mdiAccountOff } from '@mdi/js' import { mdiChevronUp, mdiRepeat, mdiDelete, mdiCalendarEdit, mdiEyeOff, mdiEye, mdiPause, mdiPlay, mdiDeleteForever, mdiScanner,
mdiMessageTextOutline, mdiAccountOff, mdiClipboardAccount } from '@mdi/js'
import { mapState } from 'vuex' import { mapState } from 'vuex'
export default { export default {
name: 'EventAdmin', name: 'EventAdmin',
data () { data () {
return { return {
mdiChevronUp, mdiRepeat, mdiDelete, mdiCalendarEdit, mdiEyeOff, mdiEye, mdiPause, mdiPlay, mdiDeleteForever, mdiScanner, mdiMessageTextOutline, mdiAccountOff, mdiChevronUp, mdiRepeat, mdiDelete, mdiCalendarEdit, mdiEyeOff, mdiEye, mdiPause, mdiPlay, mdiDeleteForever, mdiScanner,
mdiMessageTextOutline, mdiAccountOff, mdiClipboardAccount
} }
}, },
props: { props: {

View file

@ -0,0 +1,57 @@
<template>
<v-card>
<v-card-title>{{$t('event.assign_to_user')}}</v-card-title>
<v-card-subtitle>{{$t('event.assign_to_user_description')}}</v-card-subtitle>
<v-card-text>
<v-form>
<v-autocomplete
v-model='selectedUser'
:items="users"
:prepend-inner-icon="mdiAccount"
hide-no-data
item-text="email"
item-value="id"
placeholder='Choose user'
:label="$t('common.user')" />
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn color="primary" @click='assign' outlined :disabled="!selectedUser || loading" :loading="loading">{{ $t('event.assign_to_user') }}</v-btn>
<v-btn color="warning" @click='$emit("close")' outlined>{{ $t('common.close') }}</v-btn>
</v-card-actions>
</v-card>
</template>
<script>
import { mdiAccount } from '@mdi/js'
export default {
data () {
return {
mdiAccount,
loading: false,
selectedUser: {},
users: []
}
},
props: {
event: { type: Object, default: () => ({ }) },
},
async mounted () {
this.users = await this.$axios.$get(`/users`)
},
methods: {
async assign () {
try {
this.loading = true
await this.$axios.$put('/event/assign_to_author', { id: this.event.id, user_id: this.selectedUser })
this.loading = false
this.$emit('close')
this.$root.$message('event.saved', { color: 'success' })
} catch(e) {
this.$root.$message(e, { color: 'warning' })
}
}
}
}
</script>

View file

@ -12,7 +12,7 @@ v-card
v-card-actions.py-4 v-card-actions.py-4
HowToArriveNav.pl-1(:place='place') HowToArriveNav.pl-1(:place='place')
v-spacer v-spacer
v-btn(@click='$emit("close")' outlined) Close v-btn(@click='$emit("close")' outlined) {{ $t('common.close') }}
</template> </template>
<script> <script>

View file

@ -147,6 +147,8 @@
"event": { "event": {
"anon": "Anon", "anon": "Anon",
"anon_description": "You can add an event without registering or logging in, but will have to wait for someone to read it,\nconfirming that it is a suitable event. It will not be possible to modify it.<br/><br/>\nYou can instead <a href='/login'>log in</a> or <a href='/register'>register</a>. Otherwise go ahead and get an answer as soon as possible. ", "anon_description": "You can add an event without registering or logging in, but will have to wait for someone to read it,\nconfirming that it is a suitable event. It will not be possible to modify it.<br/><br/>\nYou can instead <a href='/login'>log in</a> or <a href='/register'>register</a>. Otherwise go ahead and get an answer as soon as possible. ",
"assign_to_user": "Assign to user",
"assign_to_user_description": "You can assign an anon event to an user. This will allow the selected user to modify the event.",
"same_day": "on same day", "same_day": "on same day",
"what_description": "Title", "what_description": "Title",
"description_description": "Description", "description_description": "Description",

View file

@ -93,7 +93,7 @@
//- admin actions //- admin actions
template(v-if='can_edit') template(v-if='can_edit')
EventAdmin(:event='event' @openModeration='openModeration=true') EventAdmin(:event='event' @openModeration='openModeration=true' @openAssignAuthor='openAssignAuthor=true')
//- resources from fediverse //- resources from fediverse
EventResource#resources.mt-3(:event='event' v-if='showResources') EventResource#resources.mt-3(:event='event' v-if='showResources')
@ -113,6 +113,9 @@
v-dialog(v-show='settings.allow_geolocation && event.place?.latitude && event.place?.longitude' v-model='mapModal' :fullscreen='$vuetify.breakpoint.xsOnly' destroy-on-close) v-dialog(v-show='settings.allow_geolocation && event.place?.latitude && event.place?.longitude' v-model='mapModal' :fullscreen='$vuetify.breakpoint.xsOnly' destroy-on-close)
EventMapDialog(:place='event.place' @close='mapModal=false') EventMapDialog(:place='event.place' @close='mapModal=false')
v-dialog(v-if='$auth?.user?.is_admin' v-model='openAssignAuthor' :fullscreen='$vuetify.breakpoint.xsOnly' destroy-on-close width=400)
EventAssignAuthor(:event='event' @close='openAssignAuthor=false')
v-navigation-drawer(v-model='openModeration' :fullscreen='$vuetify.breakpoint.xsOnly' fixed top right width=400 temporary) v-navigation-drawer(v-model='openModeration' :fullscreen='$vuetify.breakpoint.xsOnly' fixed top right width=400 temporary)
EventModeration(:event='event' v-if='openModeration' @close='openModeration=false') EventModeration(:event='event' v-if='openModeration' @close='openModeration=false')
@ -139,6 +142,7 @@ export default {
EventAdmin, EventAdmin,
EventResource, EventResource,
EventModeration, EventModeration,
EventAssignAuthor: () => import(/* webpackChunkName: "admin" */ '@/components/EventAssignAuthor'),
EmbedEvent, EmbedEvent,
MyPicture, MyPicture,
EventMapDialog EventMapDialog
@ -160,6 +164,7 @@ export default {
showEmbed: false, showEmbed: false,
mapModal: false, mapModal: false,
openModeration: $route?.query?.moderation ? true : false, openModeration: $route?.query?.moderation ? true : false,
openAssignAuthor: false,
reporting: false reporting: false
} }
}, },

View file

@ -422,6 +422,23 @@ const eventController = {
next() next()
}, },
async assignToAuthor (req, res) {
const body = req.body
const event = await Event.findByPk(body.id)
if (!event) {
log.debug('[UPDATE] Event not found: %s', body?.id)
return res.sendStatus(404)
}
try {
await event.update({ userId: body.user_id })
return res.sendStatus(200)
} catch (e) {
log.warn(e)
return res.status(400).send(e)
}
},
async add(req, res) { async add(req, res) {
// req.err comes from multer streaming error // req.err comes from multer streaming error
if (req.err) { if (req.err) {

View file

@ -148,6 +148,7 @@ module.exports = () => {
// api.get('/event/search', eventController.search) // api.get('/event/search', eventController.search)
api.put('/event', isAuth, upload.single('image'), eventController.update) api.put('/event', isAuth, upload.single('image'), eventController.update)
api.put('/event/assign_to_author', isAdmin, eventController.assignToAuthor)
api.get('/event/import', eventController.isAnonEventAllowed, helpers.importURL) api.get('/event/import', eventController.isAnonEventAllowed, helpers.importURL)
// remove event // remove event