merge: Implement Donation URL (#343)

Reviewed-on: https://git.joinsharkey.org/Sharkey/Sharkey/pulls/343
Reviewed-by: dakkar <dakkar@noreply.git.joinshakrey.org>
Reviewed-by: Amelia Yukii <admin@transfem.org>
Reviewed-by: Marie <marie@kaifa.ch>
This commit is contained in:
Marie 2024-01-09 22:59:24 +01:00
commit 3877b74858
16 changed files with 81 additions and 3 deletions

View file

@ -973,6 +973,7 @@ neverShow: "Nicht wieder anzeigen"
remindMeLater: "Vielleicht später" remindMeLater: "Vielleicht später"
didYouLikeMisskey: "Gefällt dir Sharkey?" didYouLikeMisskey: "Gefällt dir Sharkey?"
pleaseDonate: "Sharkey ist die kostenlose Software, die von {host} verwendet wird. Wir würden uns über Spenden freuen, damit dessen Entwicklung weitergeführt werden kann!" pleaseDonate: "Sharkey ist die kostenlose Software, die von {host} verwendet wird. Wir würden uns über Spenden freuen, damit dessen Entwicklung weitergeführt werden kann!"
pleaseDonateInstance: "Du kannst {host} auch direkt unterstützen, indem du an deine Instanz Administration spendest."
roles: "Rollen" roles: "Rollen"
role: "Rolle" role: "Rolle"
noRole: "Rolle nicht gefunden" noRole: "Rolle nicht gefunden"
@ -1150,6 +1151,8 @@ impressumDescription: "In manchen Ländern, wie Deutschland und dessen Umgebung,
privacyPolicy: "Datenschutzerklärung" privacyPolicy: "Datenschutzerklärung"
privacyPolicyUrl: "Datenschutzerklärungs-URL" privacyPolicyUrl: "Datenschutzerklärungs-URL"
tosAndPrivacyPolicy: "Nutzungsbedingungen und Datenschutzerklärung" tosAndPrivacyPolicy: "Nutzungsbedingungen und Datenschutzerklärung"
donation: "Spenden"
donationUrl: "Spenden-URL"
avatarDecorations: "Profilbilddekoration" avatarDecorations: "Profilbilddekoration"
attach: "Anbringen" attach: "Anbringen"
detach: "Entfernen" detach: "Entfernen"

View file

@ -1014,6 +1014,7 @@ neverShow: "Don't show again"
remindMeLater: "Maybe later" remindMeLater: "Maybe later"
didYouLikeMisskey: "Have you taken a liking to Sharkey?" didYouLikeMisskey: "Have you taken a liking to Sharkey?"
pleaseDonate: "{host} uses the free software, Sharkey. We would highly appreciate your donations so development of Sharkey can continue!" pleaseDonate: "{host} uses the free software, Sharkey. We would highly appreciate your donations so development of Sharkey can continue!"
pleaseDonateInstance: "You can also support {host} directly by donating to your instance administration."
roles: "Roles" roles: "Roles"
role: "Role" role: "Role"
noRole: "Role not found" noRole: "Role not found"
@ -1200,6 +1201,8 @@ impressumDescription: "In some countries, like germany, the inclusion of operato
privacyPolicy: "Privacy Policy" privacyPolicy: "Privacy Policy"
privacyPolicyUrl: "Privacy Policy URL" privacyPolicyUrl: "Privacy Policy URL"
tosAndPrivacyPolicy: "Terms of Service and Privacy Policy" tosAndPrivacyPolicy: "Terms of Service and Privacy Policy"
donation: "Donate"
donationUrl: "Donation URL"
avatarDecorations: "Avatar decorations" avatarDecorations: "Avatar decorations"
attach: "Attach" attach: "Attach"
detach: "Remove" detach: "Remove"

3
locales/index.d.ts vendored
View file

@ -1023,6 +1023,7 @@ export interface Locale {
"remindMeLater": string; "remindMeLater": string;
"didYouLikeMisskey": string; "didYouLikeMisskey": string;
"pleaseDonate": string; "pleaseDonate": string;
"pleaseDonateInstance": string;
"roles": string; "roles": string;
"role": string; "role": string;
"noRole": string; "noRole": string;
@ -1209,6 +1210,8 @@ export interface Locale {
"privacyPolicy": string; "privacyPolicy": string;
"privacyPolicyUrl": string; "privacyPolicyUrl": string;
"tosAndPrivacyPolicy": string; "tosAndPrivacyPolicy": string;
"donation": string;
"donationUrl": string;
"avatarDecorations": string; "avatarDecorations": string;
"attach": string; "attach": string;
"detach": string; "detach": string;

View file

@ -1020,6 +1020,7 @@ neverShow: "今後表示しない"
remindMeLater: "また後で" remindMeLater: "また後で"
didYouLikeMisskey: "Sharkeyを気に入っていただけましたか" didYouLikeMisskey: "Sharkeyを気に入っていただけましたか"
pleaseDonate: "Sharkeyは{host}が使用している無料のソフトウェアです。これからも開発を続けられるように、ぜひ寄付をお願いします!" pleaseDonate: "Sharkeyは{host}が使用している無料のソフトウェアです。これからも開発を続けられるように、ぜひ寄付をお願いします!"
pleaseDonateInstance: "インスタンス管理への寄付によって{host}を直接サポートすることもできます。"
roles: "ロール" roles: "ロール"
role: "ロール" role: "ロール"
noRole: "ロールはありません" noRole: "ロールはありません"
@ -1206,6 +1207,8 @@ impressumDescription: "ドイツなどの一部の国と地域では表示が義
privacyPolicy: "プライバシーポリシー" privacyPolicy: "プライバシーポリシー"
privacyPolicyUrl: "プライバシーポリシーURL" privacyPolicyUrl: "プライバシーポリシーURL"
tosAndPrivacyPolicy: "利用規約・プライバシーポリシー" tosAndPrivacyPolicy: "利用規約・プライバシーポリシー"
donation: "寄付する"
donationUrl: "寄付URL"
avatarDecorations: "アイコンデコレーション" avatarDecorations: "アイコンデコレーション"
attach: "付ける" attach: "付ける"
detach: "外す" detach: "外す"
@ -2541,7 +2544,7 @@ _dataRequest:
warn: "データのリクエストは3日ごとにしかできない。" warn: "データのリクエストは3日ごとにしかできない。"
text: "データのダウンロードが完了すると、このアカウントに登録されているEメールアドレスにEメールが送信されます。" text: "データのダウンロードが完了すると、このアカウントに登録されているEメールアドレスにEメールが送信されます。"
button: "リクエスト" button: "リクエスト"
_dataSaver: _dataSaver:
_media: _media:
title: "メディアの読み込み" title: "メディアの読み込み"

View file

@ -0,0 +1,10 @@
export class AddDonationUrl1704744370000 {
name = 'AddDonationUrl1704744370000'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "donationUrl" character varying(1024)`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "donationUrl"`);
}
}

View file

@ -385,6 +385,12 @@ export class MiMeta {
}) })
public privacyPolicyUrl: string | null; public privacyPolicyUrl: string | null;
@Column('varchar', {
length: 1024,
nullable: true,
})
public donationUrl: string | null;
@Column('varchar', { @Column('varchar', {
length: 8192, length: 8192,
nullable: true, nullable: true,

View file

@ -108,6 +108,7 @@ export class NodeinfoServerService {
tosUrl: meta.termsOfServiceUrl, tosUrl: meta.termsOfServiceUrl,
privacyPolicyUrl: meta.privacyPolicyUrl, privacyPolicyUrl: meta.privacyPolicyUrl,
impressumUrl: meta.impressumUrl, impressumUrl: meta.impressumUrl,
donationUrl: meta.donationUrl,
repositoryUrl: meta.repositoryUrl, repositoryUrl: meta.repositoryUrl,
feedbackUrl: meta.feedbackUrl, feedbackUrl: meta.feedbackUrl,
disableRegistration: meta.disableRegistration, disableRegistration: meta.disableRegistration,

View file

@ -415,6 +415,10 @@ export const meta = {
type: 'string', type: 'string',
optional: false, nullable: true, optional: false, nullable: true,
}, },
donationUrl: {
type: 'string',
optional: false, nullable: true,
},
maintainerEmail: { maintainerEmail: {
type: 'string', type: 'string',
optional: false, nullable: true, optional: false, nullable: true,
@ -498,6 +502,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
repositoryUrl: instance.repositoryUrl, repositoryUrl: instance.repositoryUrl,
feedbackUrl: instance.feedbackUrl, feedbackUrl: instance.feedbackUrl,
impressumUrl: instance.impressumUrl, impressumUrl: instance.impressumUrl,
donationUrl: instance.donationUrl,
privacyPolicyUrl: instance.privacyPolicyUrl, privacyPolicyUrl: instance.privacyPolicyUrl,
disableRegistration: instance.disableRegistration, disableRegistration: instance.disableRegistration,
emailRequiredForSignup: instance.emailRequiredForSignup, emailRequiredForSignup: instance.emailRequiredForSignup,

View file

@ -105,6 +105,7 @@ export const paramDef = {
repositoryUrl: { type: 'string' }, repositoryUrl: { type: 'string' },
feedbackUrl: { type: 'string' }, feedbackUrl: { type: 'string' },
impressumUrl: { type: 'string', nullable: true }, impressumUrl: { type: 'string', nullable: true },
donationUrl: { type: 'string', nullable: true },
privacyPolicyUrl: { type: 'string', nullable: true }, privacyPolicyUrl: { type: 'string', nullable: true },
useObjectStorage: { type: 'boolean' }, useObjectStorage: { type: 'boolean' },
objectStorageBaseUrl: { type: 'string', nullable: true }, objectStorageBaseUrl: { type: 'string', nullable: true },
@ -406,6 +407,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
set.impressumUrl = ps.impressumUrl; set.impressumUrl = ps.impressumUrl;
} }
if (ps.donationUrl !== undefined) {
set.donationUrl = ps.donationUrl;
}
if (ps.privacyPolicyUrl !== undefined) { if (ps.privacyPolicyUrl !== undefined) {
set.privacyPolicyUrl = ps.privacyPolicyUrl; set.privacyPolicyUrl = ps.privacyPolicyUrl;
} }

View file

@ -291,6 +291,10 @@ export const meta = {
type: 'string', type: 'string',
optional: false, nullable: true, optional: false, nullable: true,
}, },
donationUrl: {
type: 'string',
optional: false, nullable: true,
},
logoImageUrl: { logoImageUrl: {
type: 'string', type: 'string',
optional: false, nullable: true, optional: false, nullable: true,
@ -365,6 +369,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
repositoryUrl: instance.repositoryUrl, repositoryUrl: instance.repositoryUrl,
feedbackUrl: instance.feedbackUrl, feedbackUrl: instance.feedbackUrl,
impressumUrl: instance.impressumUrl, impressumUrl: instance.impressumUrl,
donationUrl: instance.donationUrl,
privacyPolicyUrl: instance.privacyPolicyUrl, privacyPolicyUrl: instance.privacyPolicyUrl,
disableRegistration: instance.disableRegistration, disableRegistration: instance.disableRegistration,
emailRequiredForSignup: instance.emailRequiredForSignup, emailRequiredForSignup: instance.emailRequiredForSignup,

View file

@ -26,6 +26,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkLink target="_blank" url="https://ko-fi.com/transfem">{{ i18n.ts.learnMore }}</MkLink> <MkLink target="_blank" url="https://ko-fi.com/transfem">{{ i18n.ts.learnMore }}</MkLink>
</div> </div>
</div> </div>
<div v-if="instance.donationUrl" :class="$style.text">
<I18n :src="i18n.ts.pleaseDonateInstance" tag="span">
<template #host>
{{ instance.name ?? host }}
</template>
</I18n>
<div style="margin-top: 0.2em;">
<MkLink target="_blank" :url="instance.donationUrl">{{ i18n.ts.learnMore }}</MkLink>
</div>
</div>
<div class="_buttons"> <div class="_buttons">
<MkButton @click="close">{{ i18n.ts.remindMeLater }}</MkButton> <MkButton @click="close">{{ i18n.ts.remindMeLater }}</MkButton>
<MkButton @click="neverShow">{{ i18n.ts.neverShow }}</MkButton> <MkButton @click="neverShow">{{ i18n.ts.neverShow }}</MkButton>

View file

@ -123,7 +123,13 @@ function showMenu(ev) {
action: () => { action: () => {
window.open(instance.privacyPolicyUrl, '_blank', 'noopener'); window.open(instance.privacyPolicyUrl, '_blank', 'noopener');
}, },
} : undefined, (!instance.impressumUrl && !instance.tosUrl && !instance.privacyPolicyUrl) ? undefined : { type: 'divider' }, { } : undefined, (instance.donationUrl) ? {
text: i18n.ts.donation,
icon: 'ph-hand-coins ph-bold ph-lg',
action: () => {
window.open(instance.donationUrl, '_blank', 'noopener');
},
} : undefined, (!instance.impressumUrl && !instance.tosUrl && !instance.privacyPolicyUrl && !instance.donationUrl) ? undefined : { type: 'divider' }, {
text: i18n.ts.help, text: i18n.ts.help,
icon: 'ph-question ph-bold ph-lg', icon: 'ph-question ph-bold ph-lg',
action: () => { action: () => {

View file

@ -57,6 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkFolder> </MkFolder>
<FormLink v-if="instance.tosUrl" :to="instance.tosUrl" external>{{ i18n.ts.termsOfService }}</FormLink> <FormLink v-if="instance.tosUrl" :to="instance.tosUrl" external>{{ i18n.ts.termsOfService }}</FormLink>
<FormLink v-if="instance.privacyPolicyUrl" :to="instance.privacyPolicyUrl" external>{{ i18n.ts.privacyPolicy }}</FormLink> <FormLink v-if="instance.privacyPolicyUrl" :to="instance.privacyPolicyUrl" external>{{ i18n.ts.privacyPolicy }}</FormLink>
<FormLink v-if="instance.donationUrl" :to="instance.donationUrl" external>{{ i18n.ts.donation }}</FormLink>
</div> </div>
</div> </div>
</FormSection> </FormSection>

View file

@ -40,6 +40,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #caption>{{ i18n.ts.impressumDescription }}</template> <template #caption>{{ i18n.ts.impressumDescription }}</template>
</MkInput> </MkInput>
<MkInput v-model="donationUrl" type="url">
<template #label>{{ i18n.ts.donationUrl }}</template>
<template #prefix><i class="ph-link ph-bold ph-lg"></i></template>
</MkInput>
<MkTextarea v-model="pinnedUsers"> <MkTextarea v-model="pinnedUsers">
<template #label>{{ i18n.ts.pinnedUsers }}</template> <template #label>{{ i18n.ts.pinnedUsers }}</template>
<template #caption>{{ i18n.ts.pinnedUsersDescription }}</template> <template #caption>{{ i18n.ts.pinnedUsersDescription }}</template>
@ -170,6 +175,7 @@ const description = ref<string | null>(null);
const maintainerName = ref<string | null>(null); const maintainerName = ref<string | null>(null);
const maintainerEmail = ref<string | null>(null); const maintainerEmail = ref<string | null>(null);
const impressumUrl = ref<string | null>(null); const impressumUrl = ref<string | null>(null);
const donationUrl = ref<string | null>(null);
const pinnedUsers = ref<string>(''); const pinnedUsers = ref<string>('');
const cacheRemoteFiles = ref<boolean>(false); const cacheRemoteFiles = ref<boolean>(false);
const cacheRemoteSensitiveFiles = ref<boolean>(false); const cacheRemoteSensitiveFiles = ref<boolean>(false);
@ -192,6 +198,7 @@ async function init(): Promise<void> {
maintainerName.value = meta.maintainerName; maintainerName.value = meta.maintainerName;
maintainerEmail.value = meta.maintainerEmail; maintainerEmail.value = meta.maintainerEmail;
impressumUrl.value = meta.impressumUrl; impressumUrl.value = meta.impressumUrl;
donationUrl.value = meta.donationUrl;
pinnedUsers.value = meta.pinnedUsers.join('\n'); pinnedUsers.value = meta.pinnedUsers.join('\n');
cacheRemoteFiles.value = meta.cacheRemoteFiles; cacheRemoteFiles.value = meta.cacheRemoteFiles;
cacheRemoteSensitiveFiles.value = meta.cacheRemoteSensitiveFiles; cacheRemoteSensitiveFiles.value = meta.cacheRemoteSensitiveFiles;
@ -215,6 +222,7 @@ async function save(): void {
maintainerName: maintainerName.value, maintainerName: maintainerName.value,
maintainerEmail: maintainerEmail.value, maintainerEmail: maintainerEmail.value,
impressumUrl: impressumUrl.value, impressumUrl: impressumUrl.value,
donationUrl: donationUrl.value,
pinnedUsers: pinnedUsers.value.split('\n'), pinnedUsers: pinnedUsers.value.split('\n'),
cacheRemoteFiles: cacheRemoteFiles.value, cacheRemoteFiles: cacheRemoteFiles.value,
cacheRemoteSensitiveFiles: cacheRemoteSensitiveFiles.value, cacheRemoteSensitiveFiles: cacheRemoteSensitiveFiles.value,

View file

@ -102,7 +102,13 @@ export function openInstanceMenu(ev: MouseEvent) {
action: () => { action: () => {
window.open(instance.privacyPolicyUrl, '_blank', 'noopener'); window.open(instance.privacyPolicyUrl, '_blank', 'noopener');
}, },
} : undefined, (!instance.impressumUrl && !instance.tosUrl && !instance.privacyPolicyUrl) ? undefined : { type: 'divider' }, { } : undefined, (instance.donationUrl) ? {
text: i18n.ts.donation,
icon: 'ph-hand-coins ph-bold ph-lg',
action: () => {
window.open(instance.donationUrl, '_blank', 'noopener');
},
} : undefined, (!instance.impressumUrl && !instance.tosUrl && !instance.privacyPolicyUrl && !instance.donationUrl) ? undefined : { type: 'divider' }, {
text: i18n.ts.help, text: i18n.ts.help,
icon: 'ph-question ph-bold ph-lg', icon: 'ph-question ph-bold ph-lg',
action: () => { action: () => {

View file

@ -4631,6 +4631,7 @@ export type operations = {
description: string | null; description: string | null;
disableRegistration: boolean; disableRegistration: boolean;
impressumUrl: string | null; impressumUrl: string | null;
donationUrl: string | null;
maintainerEmail: string | null; maintainerEmail: string | null;
maintainerName: string | null; maintainerName: string | null;
name: string | null; name: string | null;
@ -8650,6 +8651,7 @@ export type operations = {
repositoryUrl?: string; repositoryUrl?: string;
feedbackUrl?: string; feedbackUrl?: string;
impressumUrl?: string | null; impressumUrl?: string | null;
donationUrl?: string | null;
privacyPolicyUrl?: string | null; privacyPolicyUrl?: string | null;
useObjectStorage?: boolean; useObjectStorage?: boolean;
objectStorageBaseUrl?: string | null; objectStorageBaseUrl?: string | null;
@ -19400,6 +19402,7 @@ export type operations = {
}; };
backgroundImageUrl: string | null; backgroundImageUrl: string | null;
impressumUrl: string | null; impressumUrl: string | null;
donationUrl: string | null;
logoImageUrl: string | null; logoImageUrl: string | null;
privacyPolicyUrl: string | null; privacyPolicyUrl: string | null;
serverRules: string[]; serverRules: string[];