mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-01 03:26:04 +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;
|
let flagReason;
|
||||||
|
|
||||||
Flag.showFlagModal = function (data) {
|
Flag.showFlagModal = function (data) {
|
||||||
|
data.remote = URL.canParse(data.id) ? new URL(data.id).hostname : false;
|
||||||
|
|
||||||
app.parseAndTranslate('modals/flag', data, function (html) {
|
app.parseAndTranslate('modals/flag', data, function (html) {
|
||||||
flagModal = html;
|
flagModal = html;
|
||||||
flagModal.on('hidden.bs.modal', function () {
|
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') {
|
if (selected.attr('id') === 'flag-reason-other') {
|
||||||
reason = flagReason.val();
|
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 () {
|
flagModal.on('click', '#flag-reason-other', function () {
|
||||||
flagReason.focus();
|
flagReason.focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
flagModal.modal('show');
|
flagModal.modal('show');
|
||||||
hooks.fire('action:flag.showModal', {
|
hooks.fire('action:flag.showModal', {
|
||||||
modalEl: flagModal,
|
modalEl: flagModal,
|
||||||
type: data.type,
|
type: data.type,
|
||||||
id: data.id,
|
id: data.id,
|
||||||
|
remote: data.remote,
|
||||||
});
|
});
|
||||||
|
|
||||||
flagModal.find('#flag-reason-custom').on('keyup blur change', checkFlagButtonEnable);
|
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) {
|
Flag.rescindByType = function (type, id) {
|
||||||
api.del(`/flags/${type}/${id}/report`).then(() => {
|
api.del(`/flags/${type}/${encodeURIComponent(id)}/report`).then(() => {
|
||||||
alerts.success('[[flags:rescinded]]');
|
alerts.success('[[flags:rescinded]]');
|
||||||
hooks.fire('action:flag.rescinded', { type: type, id: id });
|
hooks.fire('action:flag.rescinded', { type: type, id: id });
|
||||||
if (type === 'post') {
|
if (type === 'post') {
|
||||||
@@ -88,11 +93,11 @@ define('flags', ['hooks', 'components', 'api', 'alerts'], function (hooks, compo
|
|||||||
}).catch(alerts.error);
|
}).catch(alerts.error);
|
||||||
};
|
};
|
||||||
|
|
||||||
function createFlag(type, id, reason) {
|
function createFlag(type, id, reason, notifyRemote = false) {
|
||||||
if (!type || !id || !reason) {
|
if (!type || !id || !reason) {
|
||||||
return;
|
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) {
|
api.post('/flags', data, function (err, flagId) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return alerts.error(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]]');
|
throw new Error('[[error:invalid-data]]');
|
||||||
}
|
}
|
||||||
|
|
||||||
const { type, id, reason } = data;
|
const { type, id, reason, notifyRemote } = data;
|
||||||
|
|
||||||
await flags.validate({
|
await flags.validate({
|
||||||
uid: caller.uid,
|
uid: caller.uid,
|
||||||
@@ -19,7 +19,7 @@ flagsApi.create = async (caller, data) => {
|
|||||||
id: id,
|
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);
|
flags.notify(flagObj, caller.uid);
|
||||||
|
|
||||||
return flagObj;
|
return flagObj;
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ const helpers = require('../helpers');
|
|||||||
const Flags = module.exports;
|
const Flags = module.exports;
|
||||||
|
|
||||||
Flags.create = async (req, res) => {
|
Flags.create = async (req, res) => {
|
||||||
const { type, id, reason } = req.body;
|
const { type, id, reason, notifyRemote } = req.body;
|
||||||
const flagObj = await api.flags.create(req, { type, id, reason });
|
const flagObj = await api.flags.create(req, { type, id, reason, notifyRemote });
|
||||||
helpers.formatApiResponse(200, res, await user.isPrivileged(req.uid) ? flagObj : undefined);
|
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 });
|
await api.flags.rescind(req, { flagId: req.params.flagId });
|
||||||
helpers.formatApiResponse(200, res);
|
helpers.formatApiResponse(200, res);
|
||||||
};
|
};
|
||||||
|
|
||||||
Flags.rescindPost = async (req, res) => {
|
Flags.rescindPost = async (req, res) => {
|
||||||
await api.flags.rescindPost(req, { pid: req.params.pid });
|
await api.flags.rescindPost(req, { pid: req.params.pid });
|
||||||
helpers.formatApiResponse(200, res);
|
helpers.formatApiResponse(200, res);
|
||||||
};
|
};
|
||||||
|
|
||||||
Flags.rescindUser = async (req, res) => {
|
Flags.rescindUser = async (req, res) => {
|
||||||
await api.flags.rescindUser(req, { uid: req.params.uid });
|
await api.flags.rescindUser(req, { uid: req.params.uid });
|
||||||
helpers.formatApiResponse(200, res);
|
helpers.formatApiResponse(200, res);
|
||||||
|
|||||||
17
src/flags.js
17
src/flags.js
@@ -4,6 +4,8 @@ const _ = require('lodash');
|
|||||||
const winston = require('winston');
|
const winston = require('winston');
|
||||||
const validator = require('validator');
|
const validator = require('validator');
|
||||||
|
|
||||||
|
const activitypub = require('./activitypub');
|
||||||
|
const activitypubApi = require('./api/activitypub');
|
||||||
const db = require('./database');
|
const db = require('./database');
|
||||||
const user = require('./user');
|
const user = require('./user');
|
||||||
const groups = require('./groups');
|
const groups = require('./groups');
|
||||||
@@ -389,7 +391,7 @@ Flags.deleteNote = async function (flagId, datetime) {
|
|||||||
await db.sortedSetRemove(`flag:${flagId}:notes`, note[0]);
|
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;
|
let doHistoryAppend = false;
|
||||||
if (!timestamp) {
|
if (!timestamp) {
|
||||||
timestamp = Date.now();
|
timestamp = Date.now();
|
||||||
@@ -474,6 +476,11 @@ Flags.create = async function (type, id, uid, reason, timestamp, forceFlag = fal
|
|||||||
|
|
||||||
const flagObj = await Flags.get(flagId);
|
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 });
|
plugins.hooks.fire('action:flags.create', { flag: flagObj });
|
||||||
return flagObj;
|
return flagObj;
|
||||||
};
|
};
|
||||||
@@ -681,6 +688,14 @@ Flags.targetExists = async function (type, id) {
|
|||||||
if (type === 'post') {
|
if (type === 'post') {
|
||||||
return await posts.exists(id);
|
return await posts.exists(id);
|
||||||
} else if (type === 'user') {
|
} 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);
|
return await user.exists(id);
|
||||||
}
|
}
|
||||||
throw new Error('[[error:invalid-data]]');
|
throw new Error('[[error:invalid-data]]');
|
||||||
|
|||||||
@@ -353,7 +353,8 @@ module.exports = function (User) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
User.setUserFields = async function (uid, data) {
|
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)) {
|
for (const [field, value] of Object.entries(data)) {
|
||||||
plugins.hooks.fire('action:user.set', { uid, field, value, type: 'set' });
|
plugins.hooks.fire('action:user.set', { uid, field, value, type: 'set' });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,9 +31,17 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
<textarea class="form-control" id="flag-reason-custom" placeholder="[[flags:modal-reason-custom]]" disabled="disabled"></textarea>
|
||||||
</div>
|
</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>
|
<button type="button" class="btn btn-primary" id="flag-post-commit" disabled>[[flags:modal-submit]]</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user