mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-02 12:05:57 +01:00
feat: federate flag creation
This commit is contained in:
@@ -8,6 +8,8 @@ define('flags', ['hooks', 'components', 'api', 'alerts'], function (hooks, compo
|
||||
let flagReason;
|
||||
|
||||
Flag.showFlagModal = function (data) {
|
||||
data.remote = URL.canParse(data.id) ? new URL(data.id).hostname : false;
|
||||
|
||||
app.parseAndTranslate('modals/flag', data, function (html) {
|
||||
flagModal = html;
|
||||
flagModal.on('hidden.bs.modal', function () {
|
||||
@@ -35,18 +37,21 @@ define('flags', ['hooks', 'components', 'api', 'alerts'], function (hooks, compo
|
||||
if (selected.attr('id') === 'flag-reason-other') {
|
||||
reason = flagReason.val();
|
||||
}
|
||||
createFlag(data.type, data.id, reason);
|
||||
const notifyRemote = $('input[name="flag-notify-remote"]').is(':checked');
|
||||
createFlag(data.type, data.id, reason, notifyRemote);
|
||||
});
|
||||
|
||||
flagModal.on('click', '#flag-reason-other', function () {
|
||||
flagReason.focus();
|
||||
});
|
||||
|
||||
|
||||
flagModal.modal('show');
|
||||
hooks.fire('action:flag.showModal', {
|
||||
modalEl: flagModal,
|
||||
type: data.type,
|
||||
id: data.id,
|
||||
remote: data.remote,
|
||||
});
|
||||
|
||||
flagModal.find('#flag-reason-custom').on('keyup blur change', checkFlagButtonEnable);
|
||||
@@ -63,7 +68,7 @@ define('flags', ['hooks', 'components', 'api', 'alerts'], function (hooks, compo
|
||||
};
|
||||
|
||||
Flag.rescindByType = function (type, id) {
|
||||
api.del(`/flags/${type}/${id}/report`).then(() => {
|
||||
api.del(`/flags/${type}/${encodeURIComponent(id)}/report`).then(() => {
|
||||
alerts.success('[[flags:rescinded]]');
|
||||
hooks.fire('action:flag.rescinded', { type: type, id: id });
|
||||
if (type === 'post') {
|
||||
@@ -88,11 +93,11 @@ define('flags', ['hooks', 'components', 'api', 'alerts'], function (hooks, compo
|
||||
}).catch(alerts.error);
|
||||
};
|
||||
|
||||
function createFlag(type, id, reason) {
|
||||
function createFlag(type, id, reason, notifyRemote = false) {
|
||||
if (!type || !id || !reason) {
|
||||
return;
|
||||
}
|
||||
const data = { type: type, id: id, reason: reason };
|
||||
const data = { type: type, id: id, reason: reason, notifyRemote: notifyRemote };
|
||||
api.post('/flags', data, function (err, flagId) {
|
||||
if (err) {
|
||||
return alerts.error(err);
|
||||
|
||||
@@ -214,3 +214,19 @@ activitypubApi.undo.like = enabledCheck(async (caller, { pid }) => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
activitypubApi.flag = enabledCheck(async (caller, flag) => {
|
||||
if (!activitypub.helpers.isUri(flag.targetId)) {
|
||||
return;
|
||||
}
|
||||
const reportedIds = [flag.targetId];
|
||||
if (flag.type === 'post' && activitypub.helpers.isUri(flag.targetUid)) {
|
||||
reportedIds.push(flag.targetUid);
|
||||
}
|
||||
const reason = flag.reports.filter(report => report.reporter.uid === caller.uid).at(-1);
|
||||
await activitypub.send('uid', caller.uid, reportedIds, {
|
||||
type: 'Flag',
|
||||
object: reportedIds,
|
||||
content: reason ? reason.value : undefined,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ flagsApi.create = async (caller, data) => {
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
|
||||
const { type, id, reason } = data;
|
||||
const { type, id, reason, notifyRemote } = data;
|
||||
|
||||
await flags.validate({
|
||||
uid: caller.uid,
|
||||
@@ -19,7 +19,7 @@ flagsApi.create = async (caller, data) => {
|
||||
id: id,
|
||||
});
|
||||
|
||||
const flagObj = await flags.create(type, id, caller.uid, reason);
|
||||
const flagObj = await flags.create(type, id, caller.uid, reason, undefined, undefined, notifyRemote);
|
||||
flags.notify(flagObj, caller.uid);
|
||||
|
||||
return flagObj;
|
||||
|
||||
@@ -7,8 +7,8 @@ const helpers = require('../helpers');
|
||||
const Flags = module.exports;
|
||||
|
||||
Flags.create = async (req, res) => {
|
||||
const { type, id, reason } = req.body;
|
||||
const flagObj = await api.flags.create(req, { type, id, reason });
|
||||
const { type, id, reason, notifyRemote } = req.body;
|
||||
const flagObj = await api.flags.create(req, { type, id, reason, notifyRemote });
|
||||
helpers.formatApiResponse(200, res, await user.isPrivileged(req.uid) ? flagObj : undefined);
|
||||
};
|
||||
|
||||
@@ -36,12 +36,10 @@ Flags.rescind = async (req, res) => {
|
||||
await api.flags.rescind(req, { flagId: req.params.flagId });
|
||||
helpers.formatApiResponse(200, res);
|
||||
};
|
||||
|
||||
Flags.rescindPost = async (req, res) => {
|
||||
await api.flags.rescindPost(req, { pid: req.params.pid });
|
||||
helpers.formatApiResponse(200, res);
|
||||
};
|
||||
|
||||
Flags.rescindUser = async (req, res) => {
|
||||
await api.flags.rescindUser(req, { uid: req.params.uid });
|
||||
helpers.formatApiResponse(200, res);
|
||||
|
||||
17
src/flags.js
17
src/flags.js
@@ -4,6 +4,8 @@ const _ = require('lodash');
|
||||
const winston = require('winston');
|
||||
const validator = require('validator');
|
||||
|
||||
const activitypub = require('./activitypub');
|
||||
const activitypubApi = require('./api/activitypub');
|
||||
const db = require('./database');
|
||||
const user = require('./user');
|
||||
const groups = require('./groups');
|
||||
@@ -389,7 +391,7 @@ Flags.deleteNote = async function (flagId, datetime) {
|
||||
await db.sortedSetRemove(`flag:${flagId}:notes`, note[0]);
|
||||
};
|
||||
|
||||
Flags.create = async function (type, id, uid, reason, timestamp, forceFlag = false) {
|
||||
Flags.create = async function (type, id, uid, reason, timestamp, forceFlag = false, notifyRemote = false) {
|
||||
let doHistoryAppend = false;
|
||||
if (!timestamp) {
|
||||
timestamp = Date.now();
|
||||
@@ -474,6 +476,11 @@ Flags.create = async function (type, id, uid, reason, timestamp, forceFlag = fal
|
||||
|
||||
const flagObj = await Flags.get(flagId);
|
||||
|
||||
if (notifyRemote && activitypub.helpers.isUri(id)) {
|
||||
const caller = await user.getUserData(uid);
|
||||
activitypubApi.flag(caller, flagObj);
|
||||
}
|
||||
|
||||
plugins.hooks.fire('action:flags.create', { flag: flagObj });
|
||||
return flagObj;
|
||||
};
|
||||
@@ -681,6 +688,14 @@ Flags.targetExists = async function (type, id) {
|
||||
if (type === 'post') {
|
||||
return await posts.exists(id);
|
||||
} else if (type === 'user') {
|
||||
if (activitypub.helpers.isUri(id)) {
|
||||
try {
|
||||
const actor = await activitypub.get('uid', 0, id);
|
||||
return !!actor;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return await user.exists(id);
|
||||
}
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
|
||||
@@ -353,7 +353,8 @@ module.exports = function (User) {
|
||||
};
|
||||
|
||||
User.setUserFields = async function (uid, data) {
|
||||
await db.setObject(`user:${uid}`, data);
|
||||
const userKey = isFinite(uid) ? `user:${uid}` : `userRemote:${uid}`;
|
||||
await db.setObject(userKey, data);
|
||||
for (const [field, value] of Object.entries(data)) {
|
||||
plugins.hooks.fire('action:user.set', { uid, field, value, type: 'set' });
|
||||
}
|
||||
|
||||
@@ -31,9 +31,17 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="mb-2">
|
||||
<textarea class="form-control" id="flag-reason-custom" placeholder="[[flags:modal-reason-custom]]" disabled="disabled"></textarea>
|
||||
</div>
|
||||
{{{ if remote }}}
|
||||
<div class="form-check form-switch mb-3">
|
||||
<input class="form-check-input" type="checkbox" name="flag-notify-remote" checked="checked">
|
||||
<label class="form-check-label text-sm" for="flag-notify-remote">
|
||||
[[flags:modal-notify-remote, {remote}]]
|
||||
</label>
|
||||
</div>
|
||||
{{{ end }}}
|
||||
|
||||
<button type="button" class="btn btn-primary" id="flag-post-commit" disabled>[[flags:modal-submit]]</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user