mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 19:15:58 +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