diff --git a/components/EventModeration.vue b/components/EventModeration.vue
index cb470d34..56eec195 100644
--- a/components/EventModeration.vue
+++ b/components/EventModeration.vue
@@ -2,21 +2,20 @@
{{$t('common.moderation')}}
-
-
+
+
{{$t('event.send_to_admins')}}
- {{$t('event.send_to_author_too')}}
+ {{$t('event.send_to_author_too')}}
send
-
+
{{ item.message }}
- {{ $time.format(item.createdAt, 'EEEE d MMMM HH:mm') }} / {{ item.author }}
+ {{ $time.format(item.createdAt, 'EEEE d MMMM HH:mm') }} / {{ item.author }}
- {{$t('event.disable_author')}}
@@ -44,20 +43,12 @@ export default {
async mounted () {
this.messages = await this.$axios.$get(`/event/messages/${this.event.id}`)
},
+ computed: {
+ isAdmin () {
+ return this.$auth.user.is_admin || this.$auth.user.is_editor
+ }
+ },
methods: {
- async disableAuthor () {
- // ask confirmation only to disable
- const ret = await this.$root.$confirm('admin.disable_user_confirm')
- if (!ret) { return }
- this.loading = true
- try {
- await this.$axios.$put(`/event/disable_author/${this.event.id}`)
- this.$root.$message("Author disabled!", { color: 'success' })
- } catch (e) {
- this.$root.$message(e, { color: 'warning' })
- }
- this.loading = false
- },
async sendMessage (is_author_visible) {
try {
this.loading = true
@@ -96,8 +87,19 @@ export default {
}
.eventModeration .messageList .v-list-item {
- border-top: 1px solid rgba(100,100,100,.3);
+ border-top: 1px solid rgba(100,100,100,.07);
word-break: break-word;
}
+.eventModeration .messageList .v-list-item.ADMIN {
+ background-color: rgba(200,100,100,0.1);
+ border-left: 2px solid rgba(255,69,0, 0.3);
+}
+
+.eventModeration .messageList .v-list-item.AUTHOR {
+ border-left: 2px solid lightblue;
+ background-color: rgba(35, 193, 255, 0.1);
+}
+
+
\ No newline at end of file
diff --git a/components/admin/Settings.vue b/components/admin/Settings.vue
index 17f32880..1402368b 100644
--- a/components/admin/Settings.vue
+++ b/components/admin/Settings.vue
@@ -58,6 +58,8 @@ v-container
v-switch.mt-1(v-model='allow_geolocation'
inset
+ persistent-hint
+ :hint="$t('admin.allow_geolocation_hint')"
:label="$t('admin.allow_geolocation')")
v-switch.mt-1(v-model='enable_moderation'
@@ -66,6 +68,12 @@ v-container
:hint="$t('admin.enable_moderation_hint')"
:label="$t('admin.enable_moderation')")
+ v-switch.mt-1(v-model='enable_report'
+ v-if="enable_moderation"
+ inset
+ persistent-hint
+ :hint="$t('admin.enable_report_hint')"
+ :label="$t('admin.enable_report')")
v-dialog(v-model='showSMTP' destroy-on-close max-width='700px' :fullscreen='$vuetify.breakpoint.xsOnly')
SMTP(@close='showSMTP = false')
@@ -145,6 +153,10 @@ export default {
get () { return this.settings.enable_moderation },
set (value) { this.setSetting({ key: 'enable_moderation', value }) }
},
+ enable_report: {
+ get () { return this.settings.enable_report },
+ set (value) { this.setSetting({ key: 'enable_report', value }) }
+ },
filteredTimezones () {
const current_timezone = DateTime.local().zoneName
tzNames.unshift(current_timezone)
diff --git a/locales/email/en.json b/locales/email/en.json
index 54cb5697..917f8570 100644
--- a/locales/email/en.json
+++ b/locales/email/en.json
@@ -27,7 +27,9 @@
"content": "This is a test email, if you are reading this your configuration is working."
},
"report": {
- "subject": "Report event [{{event.title}}] from {{author}}",
- "content": "{{author}} reported the event {{event.title}} with the following message:
{{message}}
Open moderation"
+ "subject": "Event moderation [{{event.title}}]",
+ "content_ADMIN": "An admin commented about the event {{event.title}}:
{{message}}
Open moderation",
+ "content_ANON": "A visitor reported the event {{event.title}}:
{{message}}
Open moderation",
+ "content_AUTHOR": "The author of event {{event.title}} wrote:
{{message}}
Open moderation"
}
}
diff --git a/locales/en.json b/locales/en.json
index ccf81e59..44fa1de1 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -4,6 +4,7 @@
"next": "Next",
"export": "Export",
"send": "Send",
+ "sent": "Sent",
"where": "Where",
"address": "Address",
"when": "When",
@@ -204,10 +205,12 @@
"address_geocoded_disclaimer": "If you cannot find the street address or the housenumber you are looking for in the geocoding results, you can manually insert them in the 'Address' field without loose the coordinates. Consider also that the OpenStreetMap project is open to contributions. If you have Android, we recommend StreetComplete ",
"message": "Message",
"message_hint": "You could send a message to other admins and optionally to the person who authored the event.",
- "report_message_confirmation": "Do you think this event should not be here? Write to us why",
+ "message_author_hint": "Write a message to admins",
+ "report_message_confirmation": "Do you think this event should not be here? Write us a reason",
"send_to_admins": "Send to admins",
"send_to_author_too": "Send to admins and author",
- "disable_author": "Disable the event author"
+ "disable_author": "Disable author",
+ "disable_author_confirm": "Are you sure you want to disable the author of this event?"
},
"admin": {
"place_description": "If you have gotten the place or address wrong, you can change it.
All current and past events associated with this place will change address.",
@@ -225,12 +228,14 @@
"allow_registration_description": "Allow open registrations?",
"allow_anon_event": "Allow anonymous events (has to be confirmed)?",
"enable_moderation": "Enable moderation",
- "enable_moderation_hint": "Anyone could report an event, admins are notified and could talk each other optionally including event's author",
+ "enable_moderation_hint": "Admins could talk each about events, optionally including event's author",
+ "enable_report": "Enable report event",
+ "enable_report_hint": "Any visitor could report an event, admins are notified via e-mail",
"allow_multidate_event": "Allow multi-day events",
"allow_recurrent_event": "Allow recurring events",
- "allow_online_event": "Allow online events",
- "allow_online_event_hint": "Ask for urls ",
+ "allow_online_event": "Allow online events",
"allow_geolocation": "Allow events geolocation",
+ "allow_geolocation_hint": "Optionally set the exact location of an event on a geographic map",
"recurrent_event_visible": "Show recurring events by default",
"federation": "Federation / ActivityPub",
"enable_federation": "Turn on federation",
@@ -238,7 +243,7 @@
"add_instance": "Add instance",
"select_instance_timezone": "Time zone",
"enable_resources": "Turn on resources",
- "enable_resources_help": "Allows adding resources to the event from the fediverse",
+ "enable_resources_help": "Allows adding audio, images and comments to the event from the fediverse",
"hide_boost_bookmark": "Hides boost/bookmarks",
"hide_boost_bookmark_help": "Hides the small icons showing the number of boosts and bookmarks coming from the fediverse",
"block": "Block",
diff --git a/pages/event/_slug.vue b/pages/event/_slug.vue
index 57096550..8f7fc8f2 100644
--- a/pages/event/_slug.vue
+++ b/pages/event/_slug.vue
@@ -69,7 +69,7 @@
v-list-item-title(v-text="$t('common.add_to_calendar')")
//- Report
- v-list-item(v-if='settings.enable_moderation && !showModeration' @click='report')
+ v-list-item(v-if='settings.enable_moderation && settings.enable_report && !showModeration' @click='report')
v-list-item-icon
v-icon(v-text='mdiMessageTextOutline')
v-list-item-content
@@ -159,7 +159,8 @@ export default {
event: {},
showEmbed: false,
mapModal: false,
- openModeration: false
+ openModeration: false,
+ reporting: false
}
},
head () {
@@ -277,11 +278,12 @@ export default {
},
methods: {
async report () {
- const message = await this.$root.$prompt(this.$t('event.report_message_confirmation'), { title: this.$t('common.report')})
+ this.reporting = true
+ const message = await this.$root.$prompt(this.$t('event.report_message_confirmation'), { title: this.$t('common.report') })
if (!message) {
return
}
-
+ this.reporting = false
try {
await this.$axios.$post(`/event/messages/${this.event.id}`, { message })
this.$root.$message('common.sent', { color: 'success' })
@@ -290,6 +292,9 @@ export default {
}
},
keyDown (ev) {
+ if (this.openModeration || this.reporting) {
+ return
+ }
if (ev.altKey || ev.ctrlKey || ev.metaKey || ev.shiftKey) { return }
if (ev.key === 'ArrowRight' && this.event.next) {
this.goNext()
diff --git a/server/api/controller/event.js b/server/api/controller/event.js
index 751cd91d..c8f7275c 100644
--- a/server/api/controller/event.js
+++ b/server/api/controller/event.js
@@ -126,6 +126,7 @@ const eventController = {
include: [
{ model: Tag, required: false, attributes: ['tag'], through: { attributes: [] } },
{ model: Place, attributes: ['name', 'address', 'latitude', 'longitude', 'id'] },
+ { model: User, required: false, attributes: ['is_active'] },
{
model: Resource,
where: !is_admin && { hidden: false },
@@ -135,7 +136,7 @@ const eventController = {
},
{ model: Event, required: false, as: 'parent', attributes: ['id', 'recurrent', 'is_visible', 'start_datetime'] },
],
- order: [[Resource, 'id', 'DESC']]
+ order: [[Resource, 'id', 'DESC']],
})
} catch (e) {
log.error('[EVENT]', e)
@@ -184,9 +185,10 @@ const eventController = {
if (event && (event.is_visible || is_admin)) {
event = event.get()
event.isMine = event.userId === req.user?.id
- event.isAnon = event.userId === null
+ event.isAnon = event.userId === null || !event?.user?.is_active
event.original_url = event?.ap_object?.url || event?.ap_object?.id
- delete event.ap_object
+ delete event.ap_object
+ delete event.user
delete event.userId
event.next = next && (next.slug || next.id)
event.prev = prev && (prev.slug || prev.id)
@@ -263,7 +265,6 @@ const eventController = {
return res.json(messages)
}
- log.debug('userId: %s event ud %s', event, req.user.id)
return res.sendStatus(400)
},
@@ -275,9 +276,9 @@ const eventController = {
}
const eventId = Number(req.params.event_id)
- const event = await Event.findByPk(eventId)
+ const event = await Event.findByPk(eventId, { include: [{ model: User, required: false }], raw: true })
if (!event) {
- log.warn(`Trying to ... ${eventId}`)
+ log.warn(`[REPORT] Event does not exists: ${eventId}`)
return res.sendStatus(404)
}
@@ -285,12 +286,10 @@ const eventController = {
const isMine = req.user?.id === event.userId
const isAdminOrEditor = req.user?.is_editor || req.user?.is_admin
- // if (!isAdminOrEditor && !isMine) {
- // log.warn(`Someone not allowed is trying to report on an event -> "${event.title}" isMine: ${isMine} `)
- // return res.sendStatus(403)
- // }
-
- // mail.send(user.email, 'message', { subject, message }, res.locals.settings.locale)
+ if (!isAdminOrEditor && !isMine && !res.locals.settings.enable_report) {
+ log.warn(`[REPORT] Someone not allowed is trying to report an event -> "${event.title}" isMine: ${isMine} `)
+ return res.sendStatus(403)
+ }
const author = isAdminOrEditor ? 'ADMIN' : isMine ? 'AUTHOR' : 'ANON'
try {
@@ -302,12 +301,18 @@ const eventController = {
})
const admins = await User.findAll({ where: { role: ['admin', 'editor'], is_active: true }, attributes: ['email'], raw: true })
- console.error(admins)
let emails = [res.locals.settings.admin_email]
- emails = emails.concat(admins.map(a => a.email))
+ emails = emails.concat(admins?.map(a => a.email))
log.info('[EVENT] Report event to %s', emails)
+
+ // notify admins
mail.send(emails, 'report', { event, message: body.message, author })
+ // notify author
+ if (event['user.email'] && body.is_author_visible && !isMine) {
+ mail.send(event['user.email'], 'report', { event, message: body.message, author })
+ }
+
return res.json(message)
} catch (e) {
log.warn(`[EVENT] ${e}`)
diff --git a/server/api/controller/oauth.js b/server/api/controller/oauth.js
index 4dfb696e..5853ca36 100644
--- a/server/api/controller/oauth.js
+++ b/server/api/controller/oauth.js
@@ -89,7 +89,7 @@ passport.use(new ClientPublicStrategy(verifyPublicClient))
async function verifyToken (req, accessToken, done) {
const token = await OAuthToken.findByPk(accessToken,
- { include: [{ model: User, attributes: { exclude: ['password'] } }, { model: OAuthClient, as: 'client' }] })
+ { include: [{ required: true, where: { is_active: true } , model: User, attributes: { exclude: ['password'] } }, { model: OAuthClient, as: 'client' }] })
if (!token) return done(null, false)
if (token.userId) {
diff --git a/server/api/controller/settings.js b/server/api/controller/settings.js
index 82cae812..b4fdccb2 100644
--- a/server/api/controller/settings.js
+++ b/server/api/controller/settings.js
@@ -27,7 +27,8 @@ const defaultSettings = {
allow_multidate_event: true,
allow_recurrent_event: false,
allow_online_event: true,
- enable_moderation: true,
+ enable_moderation: false,
+ enable_report: false,
recurrent_event_visible: false,
allow_geolocation: false,
geocoding_provider_type: 'Nominatim',
diff --git a/server/api/controller/user.js b/server/api/controller/user.js
index c0be927b..19cebaed 100644
--- a/server/api/controller/user.js
+++ b/server/api/controller/user.js
@@ -120,6 +120,7 @@ const userController = {
async remove (req, res) {
try {
let user
+ // TODO: has to unset events first!
if (req.user.is_admin && req.params.id) {
user = await User.findByPk(req.params.id)
} else {
diff --git a/server/emails/report/html.pug b/server/emails/report/html.pug
index e781ae33..b6468eab 100644
--- a/server/emails/report/html.pug
+++ b/server/emails/report/html.pug
@@ -1,3 +1,3 @@
extends ../layout.pug
block content
- p !{t('report.content', {config, author, event, message, url: `${config.baseurl}/event/${event.slug || event.id}`})}
+ p !{t(`report.content_${author}`, {config, event, message, url: `${config.baseurl}/event/${event.slug || event.id}`})}
diff --git a/server/helpers.js b/server/helpers.js
index 2c468797..71ca08ea 100644
--- a/server/helpers.js
+++ b/server/helpers.js
@@ -78,6 +78,7 @@ module.exports = {
allow_anon_event: settings.allow_anon_event,
allow_recurrent_event: settings.allow_recurrent_event,
enable_moderation: settings.enable_moderation,
+ enable_report: settings.enable_report,
allow_multidate_event: settings.allow_multidate_event,
allow_online_event: settings.allow_online_event,
recurrent_event_visible: settings.recurrent_event_visible,
diff --git a/server/notifier.js b/server/notifier.js
index f37f98f9..e4d84ed4 100644
--- a/server/notifier.js
+++ b/server/notifier.js
@@ -14,7 +14,7 @@ const notifier = {
emitter: new events.EventEmitter(),
- sendNotification (notification, event) {
+ async sendNotification (notification, event) {
const promises = []
log.info(`Send ${notification.type} notification ${notification.action}`)
let p
@@ -22,7 +22,10 @@ const notifier = {
// case 'mail': TODO: locale?
// return mail.send(notification.email, 'event', { event, notification })
case 'admin_email':
- p = mail.send(settingsController.settings.admin_email, 'event',
+ const admins = await User.findAll({ where: { role: ['admin', 'editor'], is_active: true }, attributes: ['email'], raw: true })
+ let emails = [settingsController.settings.admin_email]
+ emails = emails.concat(admins?.map(a => a.email))
+ p = mail.send(emails, 'event',
{ event, to_confirm: !event.is_visible, notification })
promises.push(p)
break
diff --git a/store/index.js b/store/index.js
index d1f67251..9fcbabaf 100644
--- a/store/index.js
+++ b/store/index.js
@@ -8,7 +8,8 @@ export const state = () => ({
instance_name: '',
allow_registration: true,
allow_anon_event: true,
- enable_moderation: true,
+ enable_moderation: false,
+ enable_report: false,
allow_multidate_event: true,
allow_recurrent_event: true,
allow_online_event: true,