implement mandatory CW for notes (resolves #910)
This commit is contained in:
parent
6f8d831e09
commit
92750240eb
29 changed files with 305 additions and 11 deletions
|
|
@ -34,6 +34,7 @@ export * as 'admin/avatar-decorations/list' from './endpoints/admin/avatar-decor
|
|||
export * as 'admin/avatar-decorations/update' from './endpoints/admin/avatar-decorations/update.js';
|
||||
export * as 'admin/captcha/current' from './endpoints/admin/captcha/current.js';
|
||||
export * as 'admin/captcha/save' from './endpoints/admin/captcha/save.js';
|
||||
export * as 'admin/cw-note' from './endpoints/admin/cw-note.js';
|
||||
export * as 'admin/cw-user' from './endpoints/admin/cw-user.js';
|
||||
export * as 'admin/decline-user' from './endpoints/admin/decline-user.js';
|
||||
export * as 'admin/delete-account' from './endpoints/admin/delete-account.js';
|
||||
|
|
|
|||
63
packages/backend/src/server/api/endpoints/admin/cw-note.ts
Normal file
63
packages/backend/src/server/api/endpoints/admin/cw-note.ts
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import type { MiNote, MiUser, NotesRepository } from '@/models/_.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
kind: 'write:admin:cw-note',
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
noteId: { type: 'string', format: 'misskey:id' },
|
||||
cw: { type: 'string', nullable: true },
|
||||
},
|
||||
required: ['noteId', 'cw'],
|
||||
} as const;
|
||||
|
||||
@Injectable()
|
||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||
constructor(
|
||||
@Inject(DI.notesRepository)
|
||||
private readonly notesRepository: NotesRepository,
|
||||
|
||||
private readonly moderationLogService: ModerationLogService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const note = await this.notesRepository.findOneOrFail({
|
||||
where: { id: ps.noteId },
|
||||
relations: { user: true },
|
||||
}) as MiNote & { user: MiUser };
|
||||
|
||||
// Skip if there's nothing to do
|
||||
if (note.mandatoryCW === ps.cw) return;
|
||||
|
||||
// Log event first.
|
||||
// This ensures that we don't "lose" the log if an error occurs
|
||||
await this.moderationLogService.log(me, 'setMandatoryCWForNote', {
|
||||
newCW: ps.cw,
|
||||
oldCW: note.mandatoryCW,
|
||||
noteId: note.id,
|
||||
noteUserId: note.user.id,
|
||||
noteUserUsername: note.user.username,
|
||||
noteUserHost: note.user.host,
|
||||
});
|
||||
|
||||
await this.notesRepository.update(ps.noteId, {
|
||||
// Collapse empty strings to null
|
||||
mandatoryCW: ps.cw || null,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -46,7 +46,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
|
||||
await this.usersRepository.update(ps.userId, {
|
||||
// Collapse empty strings to null
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||
mandatoryCW: ps.cw || null,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -224,7 +224,13 @@ export class MastodonConverters {
|
|||
// TODO avoid re-packing files for each edit
|
||||
const files = await this.driveFileEntityService.packManyByIds(edit.fileIds);
|
||||
|
||||
const cw = appendContentWarning(edit.cw, noteUser.mandatoryCW) ?? '';
|
||||
let cw = edit.cw ?? '';
|
||||
if (note.mandatoryCW) {
|
||||
cw = appendContentWarning(cw, note.mandatoryCW);
|
||||
}
|
||||
if (noteUser.mandatoryCW) {
|
||||
cw = appendContentWarning(cw, noteUser.mandatoryCW);
|
||||
}
|
||||
|
||||
const isQuote = renote && (edit.cw || edit.newText || edit.fileIds.length > 0 || note.replyId);
|
||||
const quoteUri = isQuote
|
||||
|
|
@ -299,7 +305,13 @@ export class MastodonConverters {
|
|||
? quoteUri.then(quote => this.mfmService.toMastoApiHtml(mfm.parse(text), mentionedRemoteUsers, false, quote) ?? escapeMFM(text))
|
||||
: '';
|
||||
|
||||
const cw = appendContentWarning(note.cw, noteUser.mandatoryCW) ?? '';
|
||||
let cw = note.cw ?? '';
|
||||
if (note.mandatoryCW) {
|
||||
cw = appendContentWarning(cw, note.mandatoryCW);
|
||||
}
|
||||
if (noteUser.mandatoryCW) {
|
||||
cw = appendContentWarning(cw, noteUser.mandatoryCW);
|
||||
}
|
||||
|
||||
const reblogged = await this.mastodonDataService.hasReblog(note.id, me);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue