diff --git a/public/language/en-GB/flags.json b/public/language/en-GB/flags.json index fb8708fc16..a8bd07ad4f 100644 --- a/public/language/en-GB/flags.json +++ b/public/language/en-GB/flags.json @@ -43,6 +43,9 @@ "notes": "Flag Notes", "add-note": "Add Note", "no-notes": "No shared notes.", + "delete-note-confirm": "Are you sure you want to delete this flag note?", + "note-added": "Note Added", + "note-deleted": "Note Deleted", "history": "Account & Flag History", "no-history": "No flag history.", @@ -53,7 +56,6 @@ "state-resolved": "Resolved", "state-rejected": "Rejected", "no-assignee": "Not Assigned", - "note-added": "Note Added", "modal-title": "Report Inappropriate Content", "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", diff --git a/public/src/client/flags/detail.js b/public/src/client/flags/detail.js index b847b4391f..bd509831ef 100644 --- a/public/src/client/flags/detail.js +++ b/public/src/client/flags/detail.js @@ -8,7 +8,7 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b $('#state').val(ajaxify.data.state).removeAttr('disabled'); $('#assignee').val(ajaxify.data.assignee).removeAttr('disabled'); - $('[data-action]').on('click', function () { + $('#content > div').on('click', '[data-action]', function () { var action = this.getAttribute('data-action'); var uid = $(this).parents('[data-uid]').attr('data-uid'); @@ -75,6 +75,26 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b case 'restore-post': postAction('restore', ajaxify.data.target.pid, ajaxify.data.target.tid); break; + + case 'delete-note': + var datetime = this.closest('[data-datetime]').getAttribute('data-datetime'); + bootbox.confirm('[[flags:delete-note-confirm]]', function (ok) { + if (ok) { + socket.emit('flags.deleteNote', { + flagId: ajaxify.data.flagId, + datetime: datetime, + }, function (err, payload) { + if (err) { + return app.alertError(err.message); + } + + app.alertSuccess('[[flags:note-deleted]]'); + Detail.reloadNotes(payload.notes); + Detail.reloadHistory(payload.history); + }); + } + }); + break; } }); diff --git a/src/flags.js b/src/flags.js index 6668895098..5c0987c212 100644 --- a/src/flags.js +++ b/src/flags.js @@ -228,6 +228,21 @@ Flags.validate = async function (payload) { Flags.getNotes = async function (flagId) { let notes = await db.getSortedSetRevRangeWithScores('flag:' + flagId + ':notes', 0, -1); + notes = await modifyNotes(notes); + return notes; +}; + +Flags.getNote = async function (flagId, datetime) { + let notes = await db.getSortedSetRangeByScoreWithScores('flag:' + flagId + ':notes', 0, 1, datetime, datetime); + if (!notes.length) { + throw new Error('[[error:invalid-data]]'); + } + + notes = await modifyNotes(notes); + return notes[0]; +}; + +async function modifyNotes(notes) { const uids = []; notes = notes.map(function (note) { const noteObj = JSON.parse(note.value); @@ -245,6 +260,15 @@ Flags.getNotes = async function (flagId) { note.content = validator.escape(note.content); return note; }); +} + +Flags.deleteNote = async function (flagId, datetime) { + const note = await db.getSortedSetRangeByScore('flag:' + flagId + ':notes', 0, 1, datetime, datetime); + if (!note.length) { + throw new Error('[[error:invalid-data]]'); + } + + await db.sortedSetRemove('flag:' + flagId + ':notes', note[0]); }; Flags.create = async function (type, id, uid, reason, timestamp) { diff --git a/src/socket.io/flags.js b/src/socket.io/flags.js index e2c9a56215..9d72101c37 100644 --- a/src/socket.io/flags.js +++ b/src/socket.io/flags.js @@ -62,4 +62,23 @@ SocketFlags.appendNote = async function (socket, data) { return { notes: notes, history: history }; }; +SocketFlags.deleteNote = async function (socket, data) { + if (!data || !(data.flagId && data.datetime)) { + throw new Error('[[error:invalid-data]]'); + } + + const note = await flags.getNote(data.flagId, data.datetime); + if (note.uid !== socket.uid) { + throw new Error('[[error:no-privileges]]'); + } + + await flags.deleteNote(data.flagId, data.datetime); + + const [notes, history] = await Promise.all([ + flags.getNotes(data.flagId), + flags.getHistory(data.flagId), + ]); + return { notes: notes, history: history }; +}; + require('../promisify')(SocketFlags);