diff --git a/public/language/en-GB/global.json b/public/language/en-GB/global.json index 0fdc21b84b..b5b9600449 100644 --- a/public/language/en-GB/global.json +++ b/public/language/en-GB/global.json @@ -115,6 +115,8 @@ "guest": "Guest", "guests": "Guests", "former_user": "A Former User", + "system-user": "System", + "unknown-user": "Unknown user", "updated.title": "Forum Updated", "updated.message": "This forum has just been updated to the latest version. Click here to refresh the page.", diff --git a/public/language/en-GB/topic.json b/public/language/en-GB/topic.json index 759ca53e51..ac92291087 100644 --- a/public/language/en-GB/topic.json +++ b/public/language/en-GB/topic.json @@ -22,10 +22,8 @@ "login-to-view": "🔒 Log in to view", "edit": "Edit", "delete": "Delete", - "deleted": "Deleted", "purge": "Purge", "restore": "Restore", - "restored": "Restored", "move": "Move", "change-owner": "Change Owner", "fork": "Fork", @@ -33,16 +31,21 @@ "share": "Share", "tools": "Tools", "locked": "Locked", - "unlocked": "Unlocked", "pinned": "Pinned", "pinned-with-expiry": "Pinned until %1", - "unpinned": "Unpinned", "moved": "Moved", "moved-from": "Moved from %1", "copy-ip": "Copy IP", "ban-ip": "Ban IP", "view-history": "Edit History", + "locked-by": "Locked by", + "unlocked-by": "Unlocked by", + "pinned-by": "Pinned by", + "unpinned-by": "Unpinned by", + "deleted-by": "Deleted by", + "restored-by": "Restored by", + "bookmark_instructions" : "Click here to return to the last read post in this thread.", "flag-post": "Flag this post", diff --git a/src/topics/events.js b/src/topics/events.js index 7db04751f4..dfe43967c2 100644 --- a/src/topics/events.js +++ b/src/topics/events.js @@ -1,38 +1,44 @@ 'use strict'; const db = require('../database'); +const user = require('../user'); const plugins = require('../plugins'); const Events = module.exports; +/** + * Note: Plugins! + * + * You are able to define additional topic event types here. + * Register to hook `filter:topicEvents.init` and append your custom type to the `types` object. + * You can then log a custom topic event by calling `topics.events.log(tid, { type, uid });` + * `uid` is optional; if you pass in a valid uid in the payload, the user avatar/username will be rendered as part of the event text + * + */ Events._types = { pin: { icon: 'fa-thumb-tack', - text: '[[topic:pinned]]', - }, - pin_expiry: { - icon: 'fa-thumb-tack', - text: '[[topic:pinned-with-expiry]]', + text: '[[topic:pinned-by]]', }, unpin: { icon: 'fa-thumb-tack', - text: '[[topic:unpinned]]', + text: '[[topic:unpinned-by]]', }, lock: { icon: 'fa-lock', - text: '[[topic:locked]]', + text: '[[topic:locked-by]]', }, unlock: { icon: 'fa-unlock', - text: '[[topic:unlocked]]', + text: '[[topic:unlocked-by]]', }, delete: { icon: 'fa-trash', - text: '[[topic:deleted]]', + text: '[[topic:deleted-by]]', }, restore: { icon: 'fa-trash-o', - text: '[[topic:restored]]', + text: '[[topic:restored-by]]', }, }; Events._ready = false; @@ -57,11 +63,20 @@ Events.get = async (tid) => { const eventIds = await db.getSortedSetRangeWithScores(`topic:${tid}:events`, 0, -1); const keys = eventIds.map(obj => `topicEvent:${obj.value}`); const timestamps = eventIds.map(obj => obj.score); - const events = await db.getObjects(keys); + let events = await db.getObjects(keys); + const users = await getUserInfo(events.map(event => event.uid).filter(Boolean)); + + // Remove events whose types no longer exist (e.g. plugin uninstalled) + events = events.filter(event => Events._types.hasOwnProperty(event.type)); + + // Add user & metadata events.forEach((event, idx) => { event.id = parseInt(eventIds[idx].value, 10); event.timestamp = timestamps[idx]; event.timestampISO = new Date(timestamps[idx]).toISOString(); + if (event.hasOwnProperty('uid')) { + event.user = users.get(event.uid === 'system' ? 'system' : parseInt(event.uid, 10)); + } Object.assign(event, Events._types[event.type]); }); @@ -69,6 +84,17 @@ Events.get = async (tid) => { return events; }; +async function getUserInfo(uids) { + uids = uids.filter((uid, idx) => !isNaN(parseInt(uid, 10)) && uids.indexOf(uid) === idx); + const userData = await user.getUsersFields(uids, ['picture', 'username', 'userslug']); + const userMap = userData.reduce((memo, cur) => memo.set(cur.uid, cur), new Map()); + userMap.set('system', { + system: true, + }); + + return userMap; +} + Events.log = async (tid, payload) => { await Events.init(); const topics = require('.');