mirror of
https://framagit.org/les/gancio.git
synced 2025-01-31 16:42:22 +01:00
fix: Add option to transfer anon event ownership, fix #394
This commit is contained in:
parent
bbad9d127d
commit
7b748bd49c
7 changed files with 94 additions and 5 deletions
|
@ -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: {
|
||||||
|
|
57
components/EventAssignAuthor.vue
Normal file
57
components/EventAssignAuthor.vue
Normal 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>
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue