mirror of
https://github.com/MisskeyIO/misskey
synced 2024-11-27 14:28:49 +09:00
Merge tag '2023.9.3' into merge-upstream
This commit is contained in:
commit
b4cdd35866
13
CHANGELOG.md
13
CHANGELOG.md
@ -12,6 +12,19 @@
|
||||
|
||||
-->
|
||||
|
||||
## 2023.9.3
|
||||
### General
|
||||
- Enhance: ノートの翻訳機能の利用可否をロールで設定可能に
|
||||
|
||||
### Client
|
||||
- Enhance: AiScriptでホストのアドレスを参照する定数`SERVER_URL`を追加
|
||||
- Enhance: モデレーションログ機能の強化
|
||||
- Enhance: ローカリゼーションの更新
|
||||
|
||||
### Server
|
||||
- Fix: Redisに古いバージョンのキャッシュが残っている場合、キャッシュが消えるまでの間通知が届かなくなる問題を修正
|
||||
- Fix: 後方互換性の修正
|
||||
|
||||
## 2023.9.2
|
||||
|
||||
### General
|
||||
|
@ -1123,6 +1123,9 @@ authenticationRequiredToContinue: "Bitte authentifiziere dich, um fortzufahren"
|
||||
dateAndTime: "Zeit"
|
||||
showRenotes: "Renotes anzeigen"
|
||||
edited: "Bearbeitet"
|
||||
notificationRecieveConfig: "Benachrichtigungseinstellungen"
|
||||
mutualFollow: "Gegenseitig gefolgt"
|
||||
fileAttachedOnly: "Nur Notizen mit Dateien"
|
||||
_announcement:
|
||||
forExistingUsers: "Nur für existierende Nutzer"
|
||||
forExistingUsersDescription: "Ist diese Option aktiviert, wird diese Ankündigung nur Nutzern angezeigt, die zum Zeitpunkt der Ankündigung bereits registriert sind. Ist sie deaktiviert, wird sie auch Nutzern, die sich nach dessen Veröffentlichung registrieren, angezeigt."
|
||||
@ -2132,3 +2135,6 @@ _moderationLogTypes:
|
||||
unmarkSensitiveDriveFile: "Datei als nicht sensitiv markiert"
|
||||
resolveAbuseReport: "Meldung bearbeitet"
|
||||
createInvitation: "Einladung erstellt"
|
||||
createAd: "Werbung erstellt"
|
||||
deleteAd: "Werbung gelöscht"
|
||||
updateAd: "Werbung aktualisiert"
|
||||
|
@ -1125,6 +1125,9 @@ authenticationRequiredToContinue: "Please authenticate to continue"
|
||||
dateAndTime: "Timestamp"
|
||||
showRenotes: "Show renotes"
|
||||
edited: "Edited"
|
||||
notificationRecieveConfig: "Notification Settings"
|
||||
mutualFollow: "Mutual follow"
|
||||
fileAttachedOnly: "Only notes with files"
|
||||
_announcement:
|
||||
forExistingUsers: "Existing users only"
|
||||
forExistingUsersDescription: "This announcement will only be shown to users existing at the point of publishment if enabled. If disabled, those newly signing up after it has been posted will also see it."
|
||||
@ -2138,3 +2141,6 @@ _moderationLogTypes:
|
||||
unmarkSensitiveDriveFile: "File unmarked as sensitive"
|
||||
resolveAbuseReport: "Report resolved"
|
||||
createInvitation: "Invite generated"
|
||||
createAd: "Ad created"
|
||||
deleteAd: "Ad deleted"
|
||||
updateAd: "Ad updated"
|
||||
|
1
locales/index.d.ts
vendored
1
locales/index.d.ts
vendored
@ -1568,6 +1568,7 @@ export interface Locale {
|
||||
"descriptionOfRateLimitFactor": string;
|
||||
"canHideAds": string;
|
||||
"canSearchNotes": string;
|
||||
"canUseTranslator": string;
|
||||
};
|
||||
"_condition": {
|
||||
"isLocal": string;
|
||||
|
@ -1488,7 +1488,8 @@ _role:
|
||||
rateLimitFactor: "レートリミット"
|
||||
descriptionOfRateLimitFactor: "小さいほど制限が緩和され、大きいほど制限が強化されます。"
|
||||
canHideAds: "広告の非表示"
|
||||
canSearchNotes: "ノート検索の利用可否"
|
||||
canSearchNotes: "ノート検索の利用"
|
||||
canUseTranslator: "翻訳機能の利用"
|
||||
_condition:
|
||||
isLocal: "ローカルユーザー"
|
||||
isRemote: "リモートユーザー"
|
||||
|
@ -416,6 +416,9 @@ totp: "인증 앱"
|
||||
totpDescription: "인증 앱을 사용하여 일회성 비밀번호 입력"
|
||||
moderator: "모더레이터"
|
||||
moderation: "모더레이션"
|
||||
moderationNote: "모더레이션 노트"
|
||||
addModerationNote: "모더레이션 노트 추가하기"
|
||||
moderationLogs: "모더레이션 로그"
|
||||
nUsersMentioned: "{n}명이 언급함"
|
||||
securityKeyAndPasskey: "보안 키 또는 패스 키"
|
||||
securityKey: "보안 키"
|
||||
@ -1107,6 +1110,18 @@ youHaveUnreadAnnouncements: "읽지 않은 공지사항이 있습니다."
|
||||
useSecurityKey: "브라우저 또는 기기의 안내에 따라 보안 키 또는 패스키를 사용해 주십시오."
|
||||
replies: "답글"
|
||||
renotes: "리노트"
|
||||
loadReplies: "답글 보기"
|
||||
loadConversation: "대화 보기"
|
||||
pinnedList: "고정해놓은 리스트"
|
||||
keepScreenOn: "기기 화면을 항상 켜기"
|
||||
verifiedLink: "이 링크의 소유자임이 확인되었습니다."
|
||||
notifyNotes: "새 노트 알림 켜기"
|
||||
unnotifyNotes: "새 노트 알림 끄기"
|
||||
authentication: "인증"
|
||||
showRenotes: "리노트 표시"
|
||||
edited: "수정됨"
|
||||
notificationRecieveConfig: "알림 설정"
|
||||
mutualFollow: "맞팔로우"
|
||||
_announcement:
|
||||
forExistingUsers: "기존 유저에게만 알림"
|
||||
forExistingUsersDescription: "활성화하면 이 공지사항을 게시한 시점에서 이미 가입한 유저에게만 표시합니다. 비활성화하면 게시 후에 가입한 유저에게도 표시합니다."
|
||||
@ -1135,6 +1150,12 @@ _serverRules:
|
||||
description: "회원 가입 이전에 간단하게 표시할 서버 규칙입니다. 이용 약관의 요약으로 구성하는 것을 추천합니다."
|
||||
_serverSettings:
|
||||
iconUrl: "아이콘 URL"
|
||||
appIconUsageExample: "예를 들어, PWA나 스마트폰 홈 화면에 북마크로 추가되었을 때 등"
|
||||
appIconStyleRecommendation: "아이콘이 원형 또는 둥근 사각형으로 잘리는 경우가 있으므로, 가장자리 여백이 충분한 사진을 사용하는 것을 추천합니다."
|
||||
appIconResolutionMustBe: "해상도는 반드시 {resolution} 이어야 합니다."
|
||||
manifestJsonOverride: "manifest.json 오버라이드"
|
||||
shortName: "약칭"
|
||||
shortNameDescription: "서버의 정식 명칭이 긴 경우에, 대신에 표시할 수 있는 약칭이나 통칭."
|
||||
_accountMigration:
|
||||
moveFrom: "다른 계정에서 이 계정으로 이사"
|
||||
moveFromSub: "다른 계정에 대한 별칭을 생성"
|
||||
|
@ -1120,6 +1120,9 @@ authentication: "การตรวจสอบสิทธิ์"
|
||||
dateAndTime: "เวลาประทับ"
|
||||
showRenotes: "แสดงรีโน้ต"
|
||||
edited: "แก้ไขแล้ว"
|
||||
notificationRecieveConfig: "การตั้งค่าการแจ้งเตือน"
|
||||
mutualFollow: "ติดตามซึ่งกันและกัน"
|
||||
fileAttachedOnly: "เฉพาะโน้ตที่มีไฟล์เท่านั้น"
|
||||
_announcement:
|
||||
forExistingUsers: "ผู้ใช้งานที่มีอยู่เท่านั้น"
|
||||
forExistingUsersDescription: "การประกาศนี้จะแสดงต่อผู้ใช้ที่มีอยู่ ณ จุดที่เผยแพร่นั้นๆถ้าหากเปิดใช้งาน ถ้าหากปิดใช้งานผู้ที่กำลังสมัครใหม่หลังจากโพสต์แล้วนั้นก็จะเห็นเช่นกัน"
|
||||
@ -2104,3 +2107,6 @@ _moderationLogTypes:
|
||||
resetPassword: "รีเซ็ตรหัสผ่าน"
|
||||
resolveAbuseReport: "รายงานได้รับการแก้ไขแล้ว"
|
||||
createInvitation: "สร้างคำเชิญ"
|
||||
createAd: "สร้างโฆษณาแล้ว"
|
||||
deleteAd: "ลบโฆษณาออกแล้ว"
|
||||
updateAd: "อัปเดตโฆษณาแล้ว"
|
||||
|
@ -1123,6 +1123,9 @@ authenticationRequiredToContinue: "要继续,请先进行验证"
|
||||
dateAndTime: "日期和时间"
|
||||
showRenotes: "显示转帖"
|
||||
edited: "已编辑"
|
||||
notificationRecieveConfig: "通知接收设置"
|
||||
mutualFollow: "互相关注"
|
||||
fileAttachedOnly: "仅限媒体"
|
||||
_announcement:
|
||||
forExistingUsers: "仅限现有用户"
|
||||
forExistingUsersDescription: "若启用,该公告将仅对创建此公告时存在的用户可见。 如果禁用,则在创建此公告后注册的用户也可以看到该公告。"
|
||||
@ -2130,3 +2133,6 @@ _moderationLogTypes:
|
||||
unmarkSensitiveDriveFile: "取消标记网盘文件为敏感媒体"
|
||||
resolveAbuseReport: "处理举报"
|
||||
createInvitation: "发行邀请码"
|
||||
createAd: "创建了广告"
|
||||
deleteAd: "删除了广告"
|
||||
updateAd: "更新了广告"
|
||||
|
@ -1122,6 +1122,8 @@ authentication: "驗證"
|
||||
authenticationRequiredToContinue: "請於繼續前完成驗證"
|
||||
dateAndTime: "日期與時間"
|
||||
showRenotes: "顯示轉發貼文"
|
||||
edited: "已編輯"
|
||||
mutualFollow: "互相追隨"
|
||||
_announcement:
|
||||
forExistingUsers: "僅限既有的使用者"
|
||||
forExistingUsersDescription: "啟用代表僅向現存使用者顯示;停用代表張貼後註冊的新使用者也會看到。"
|
||||
@ -2131,3 +2133,6 @@ _moderationLogTypes:
|
||||
unmarkSensitiveDriveFile: "撤銷標記為敏感檔案"
|
||||
resolveAbuseReport: "解決檢舉"
|
||||
createInvitation: "建立邀請碼"
|
||||
createAd: "建立廣告"
|
||||
deleteAd: "刪除廣告"
|
||||
updateAd: "更新廣告"
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"version": "2023.9.2-io",
|
||||
"version": "2023.9.3-io",
|
||||
"codename": "nasubi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -80,7 +80,10 @@ export class NotificationService implements OnApplicationShutdown {
|
||||
notifierId?: MiUser['id'] | null,
|
||||
): Promise<MiNotification | null> {
|
||||
const profile = await this.cacheService.userProfileCache.fetch(notifieeId);
|
||||
const recieveConfig = profile.notificationRecieveConfig[type];
|
||||
|
||||
// 古いMisskeyバージョンのキャッシュが残っている可能性がある
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const recieveConfig = (profile.notificationRecieveConfig ?? {})[type];
|
||||
if (recieveConfig?.type === 'never') {
|
||||
return null;
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ export type RolePolicies = {
|
||||
inviteExpirationTime: number;
|
||||
canManageCustomEmojis: boolean;
|
||||
canSearchNotes: boolean;
|
||||
canUseTranslator: boolean;
|
||||
canHideAds: boolean;
|
||||
driveCapacityMb: number;
|
||||
alwaysMarkNsfw: boolean;
|
||||
@ -64,6 +65,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
|
||||
inviteExpirationTime: 0,
|
||||
canManageCustomEmojis: false,
|
||||
canSearchNotes: false,
|
||||
canUseTranslator: true,
|
||||
canHideAds: false,
|
||||
driveCapacityMb: 100,
|
||||
alwaysMarkNsfw: false,
|
||||
@ -312,6 +314,7 @@ export class RoleService implements OnApplicationShutdown {
|
||||
inviteExpirationTime: calc('inviteExpirationTime', vs => Math.max(...vs)),
|
||||
canManageCustomEmojis: calc('canManageCustomEmojis', vs => vs.some(v => v === true)),
|
||||
canSearchNotes: calc('canSearchNotes', vs => vs.some(v => v === true)),
|
||||
canUseTranslator: calc('canUseTranslator', vs => vs.some(v => v === true)),
|
||||
canHideAds: calc('canHideAds', vs => vs.some(v => v === true)),
|
||||
driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)),
|
||||
alwaysMarkNsfw: calc('alwaysMarkNsfw', vs => vs.some(v => v === true)),
|
||||
|
@ -456,6 +456,7 @@ export class UserEntityService implements OnModuleInit {
|
||||
hasPendingReceivedFollowRequest: this.getHasPendingReceivedFollowRequest(user.id),
|
||||
mutedWords: profile!.mutedWords,
|
||||
mutedInstances: profile!.mutedInstances,
|
||||
mutingNotificationTypes: [], // 後方互換性のため
|
||||
notificationRecieveConfig: profile!.notificationRecieveConfig,
|
||||
emailNotificationTypes: profile!.emailNotificationTypes,
|
||||
achievements: profile!.achievements,
|
||||
|
@ -10,12 +10,13 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||
import { GetterService } from '@/server/api/GetterService.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['notes'],
|
||||
|
||||
requireCredential: false,
|
||||
requireCredential: true,
|
||||
|
||||
res: {
|
||||
type: 'object',
|
||||
@ -23,6 +24,11 @@ export const meta = {
|
||||
},
|
||||
|
||||
errors: {
|
||||
unavailable: {
|
||||
message: 'Translate of notes unavailable.',
|
||||
code: 'UNAVAILABLE',
|
||||
id: '50a70314-2d8a-431b-b433-efa5cc56444c',
|
||||
},
|
||||
noSuchNote: {
|
||||
message: 'No such note.',
|
||||
code: 'NO_SUCH_NOTE',
|
||||
@ -47,14 +53,20 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
private getterService: GetterService,
|
||||
private metaService: MetaService,
|
||||
private httpRequestService: HttpRequestService,
|
||||
private roleService: RoleService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const policies = await this.roleService.getUserPolicies(me.id);
|
||||
if (!policies.canUseTranslator) {
|
||||
throw new ApiError(meta.errors.unavailable);
|
||||
}
|
||||
|
||||
const note = await this.getterService.getNote(ps.noteId).catch(err => {
|
||||
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||
throw err;
|
||||
});
|
||||
|
||||
if (!(await this.noteEntityService.isVisibleForMe(note, me ? me.id : null))) {
|
||||
if (!(await this.noteEntityService.isVisibleForMe(note, me.id))) {
|
||||
return 204; // TODO: 良い感じのエラー返す
|
||||
}
|
||||
|
||||
|
@ -167,6 +167,7 @@ describe('ユーザー', () => {
|
||||
unreadAnnouncements: user.unreadAnnouncements,
|
||||
mutedWords: user.mutedWords,
|
||||
mutedInstances: user.mutedInstances,
|
||||
mutingNotificationTypes: user.mutingNotificationTypes,
|
||||
notificationRecieveConfig: user.notificationRecieveConfig,
|
||||
emailNotificationTypes: user.emailNotificationTypes,
|
||||
achievements: user.achievements,
|
||||
@ -416,6 +417,7 @@ describe('ユーザー', () => {
|
||||
assert.deepStrictEqual(response.unreadAnnouncements, []);
|
||||
assert.deepStrictEqual(response.mutedWords, []);
|
||||
assert.deepStrictEqual(response.mutedInstances, []);
|
||||
assert.deepStrictEqual(response.mutingNotificationTypes, []);
|
||||
assert.deepStrictEqual(response.notificationRecieveConfig, {});
|
||||
assert.deepStrictEqual(response.emailNotificationTypes, ['follow', 'receiveFollowRequest']);
|
||||
assert.deepStrictEqual(response.achievements, []);
|
||||
|
@ -71,6 +71,7 @@ export const ROLE_POLICIES = [
|
||||
'inviteExpirationTime',
|
||||
'canManageCustomEmojis',
|
||||
'canSearchNotes',
|
||||
'canUseTranslator',
|
||||
'canHideAds',
|
||||
'driveCapacityMb',
|
||||
'alwaysMarkNsfw',
|
||||
|
@ -17,8 +17,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<span v-else-if="log.type === 'suspend'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
|
||||
<span v-else-if="log.type === 'unsuspend'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
|
||||
<span v-else-if="log.type === 'resetPassword'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
|
||||
<span v-else-if="log.type === 'assignRole'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
|
||||
<span v-else-if="log.type === 'unassignRole'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
|
||||
<span v-else-if="log.type === 'assignRole'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }} <i class="ti ti-arrow-right"></i> {{ log.info.roleName }}</span>
|
||||
<span v-else-if="log.type === 'unassignRole'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }} <i class="ti ti-equal-not"></i> {{ log.info.roleName }}</span>
|
||||
<span v-else-if="log.type === 'createRole'">: {{ log.info.role.name }}</span>
|
||||
<span v-else-if="log.type === 'updateRole'">: {{ log.info.before.name }}</span>
|
||||
<span v-else-if="log.type === 'deleteRole'">: {{ log.info.role.name }}</span>
|
||||
|
@ -359,6 +359,26 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.canUseTranslator, 'canUseTranslator'])">
|
||||
<template #label>{{ i18n.ts._role._options.canUseTranslator }}</template>
|
||||
<template #suffix>
|
||||
<span v-if="role.policies.canUseTranslator.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
|
||||
<span v-else>{{ role.policies.canUseTranslator.value ? i18n.ts.yes : i18n.ts.no }}</span>
|
||||
<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canUseTranslator)"></i></span>
|
||||
</template>
|
||||
<div class="_gaps">
|
||||
<MkSwitch v-model="role.policies.canUseTranslator.useDefault" :readonly="readonly">
|
||||
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
|
||||
</MkSwitch>
|
||||
<MkSwitch v-model="role.policies.canUseTranslator.value" :disabled="role.policies.canUseTranslator.useDefault" :readonly="readonly">
|
||||
<template #label>{{ i18n.ts.enable }}</template>
|
||||
</MkSwitch>
|
||||
<MkRange v-model="role.policies.canUseTranslator.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||
</MkRange>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])">
|
||||
<template #label>{{ i18n.ts._role._options.driveCapacity }}</template>
|
||||
<template #suffix>
|
||||
|
@ -127,6 +127,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</MkSwitch>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.canUseTranslator, 'canSearchNotes'])">
|
||||
<template #label>{{ i18n.ts._role._options.canUseTranslator }}</template>
|
||||
<template #suffix>{{ policies.canUseTranslator ? i18n.ts.yes : i18n.ts.no }}</template>
|
||||
<MkSwitch v-model="policies.canUseTranslator">
|
||||
<template #label>{{ i18n.ts.enable }}</template>
|
||||
</MkSwitch>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])">
|
||||
<template #label>{{ i18n.ts._role._options.driveCapacity }}</template>
|
||||
<template #suffix>{{ policies.driveCapacityMb }}MB</template>
|
||||
|
@ -8,7 +8,7 @@ import * as os from '@/os.js';
|
||||
import { $i } from '@/account.js';
|
||||
import { miLocalStorage } from '@/local-storage.js';
|
||||
import { customEmojis } from '@/custom-emojis.js';
|
||||
import { lang } from '@/config.js';
|
||||
import { url, lang } from '@/config.js';
|
||||
|
||||
export function createAiScriptEnv(opts) {
|
||||
return {
|
||||
@ -17,6 +17,7 @@ export function createAiScriptEnv(opts) {
|
||||
USER_USERNAME: $i ? values.STR($i.username) : values.NULL,
|
||||
CUSTOM_EMOJIS: utils.jsToVal(customEmojis.value),
|
||||
LOCALE: values.STR(lang),
|
||||
SERVER_URL: values.STR(url),
|
||||
'Mk:dialog': values.FN_NATIVE(async ([title, text, type]) => {
|
||||
await os.alert({
|
||||
type: type ? type.value : 'info',
|
||||
|
@ -288,7 +288,7 @@ export function getNoteMenu(props: {
|
||||
text: i18n.ts.share,
|
||||
action: share,
|
||||
},
|
||||
instance.translatorAvailable ? {
|
||||
$i && $i.policies.canUseTranslator && instance.translatorAvailable ? {
|
||||
icon: 'ti ti-language-hiragana',
|
||||
text: i18n.ts.translate,
|
||||
action: translate,
|
||||
|
Loading…
Reference in New Issue
Block a user