diff options
| author | Acid Chicken (硫酸鶏) <root@acid-chicken.com> | 2024-02-17 13:34:50 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-02-17 13:34:50 +0900 |
| commit | acba96c1d34572ed7bd454462c2462d2a32369f4 (patch) | |
| tree | 9960987a3d6b39847bcf252514eca58650f9d59c /packages/frontend/src | |
| parent | feat: add link to local note in initial comment of abuse note (#13347) (diff) | |
| download | misskey-acba96c1d34572ed7bd454462c2462d2a32369f4.tar.gz misskey-acba96c1d34572ed7bd454462c2462d2a32369f4.tar.bz2 misskey-acba96c1d34572ed7bd454462c2462d2a32369f4.zip | |
feat: license violation protection (#13285)
* spec(frontend): aboutページにリポジトリ・フィードバックのURLを表示させる
Cherry-picked from MisskeyIO#441
Cherry-picked from MisskeyIO#438
* feat: license violation protection
* build: fix typo
* build: fix typo
* fix: farewell to the static type land
* fix: key typo
* fix: import typo
* fix: properly interpret `prominently`
* docs: add disclaimer
* docs: update CHANGELOG
* chore: add gap
---------
Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
Diffstat (limited to 'packages/frontend/src')
| -rw-r--r-- | packages/frontend/src/boot/main-boot.ts | 8 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkSourceCodeAvailablePopup.vue | 112 | ||||
| -rw-r--r-- | packages/frontend/src/local-storage.ts | 1 | ||||
| -rw-r--r-- | packages/frontend/src/pages/about-misskey.vue | 24 | ||||
| -rw-r--r-- | packages/frontend/src/pages/about.vue | 40 | ||||
| -rw-r--r-- | packages/frontend/src/pages/admin/branding.vue | 16 | ||||
| -rw-r--r-- | packages/frontend/src/pages/admin/settings.vue | 15 |
7 files changed, 206 insertions, 10 deletions
diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index afe8e2ac1b..b19d45a35e 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -11,6 +11,7 @@ import { alert, confirm, popup, post, toast } from '@/os.js'; import { useStream } from '@/stream.js'; import * as sound from '@/scripts/sound.js'; import { $i, signout, updateAccount } from '@/account.js'; +import { fetchInstance, instance } from '@/instance.js'; import { ColdDeviceStorage, defaultStore } from '@/store.js'; import { makeHotkey } from '@/scripts/hotkey.js'; import { reactionPicker } from '@/scripts/reaction-picker.js'; @@ -234,6 +235,13 @@ export async function mainBoot() { } } + fetchInstance().then(() => { + const modifiedVersionMustProminentlyOfferInAgplV3Section13Read = miLocalStorage.getItem('modifiedVersionMustProminentlyOfferInAgplV3Section13Read'); + if (modifiedVersionMustProminentlyOfferInAgplV3Section13Read !== 'true' && instance.repositoryUrl !== 'https://github.com/misskey-dev/misskey') { + popup(defineAsyncComponent(() => import('@/components/MkSourceCodeAvailablePopup.vue')), {}, {}, 'closed'); + } + }); + if ('Notification' in window) { // 許可を得ていなかったらリクエスト if (Notification.permission === 'default') { diff --git a/packages/frontend/src/components/MkSourceCodeAvailablePopup.vue b/packages/frontend/src/components/MkSourceCodeAvailablePopup.vue new file mode 100644 index 0000000000..80f3a6709c --- /dev/null +++ b/packages/frontend/src/components/MkSourceCodeAvailablePopup.vue @@ -0,0 +1,112 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div class="_panel _shadow" :class="$style.root"> + <div :class="$style.icon"> + <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-brand-open-source" width="40" height="40" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> + <path stroke="none" d="M0 0h24v24H0z" fill="none"/> + <path d="M12 3a9 9 0 0 1 3.618 17.243l-2.193 -5.602a3 3 0 1 0 -2.849 0l-2.193 5.603a9 9 0 0 1 3.617 -17.244z"/> + </svg> + </div> + <div :class="$style.main"> + <div :class="$style.title"> + <I18n :src="i18n.ts.aboutX" tag="span"> + <template #x> + {{ instance.name ?? host }} + </template> + </I18n> + </div> + <div :class="$style.text"> + <I18n :src="i18n.ts._aboutMisskey.thisIsModifiedVersion" tag="span"> + <template #name> + {{ instance.name ?? host }} + </template> + </I18n> + <I18n :src="i18n.ts.correspondingSourceIsAvailable" tag="span"> + <template #anchor> + <MkA to="/about-misskey" class="_link">{{ i18n.ts.aboutMisskey }}</MkA> + </template> + </I18n> + </div> + <div class="_buttons"> + <MkButton @click="close">{{ i18n.ts.gotIt }}</MkButton> + </div> + </div> + <button class="_button" :class="$style.close" @click="close"><i class="ti ti-x"></i></button> +</div> +</template> + +<script lang="ts" setup> +import MkButton from '@/components/MkButton.vue'; +import { host } from '@/config.js'; +import { i18n } from '@/i18n.js'; +import { instance } from '@/instance.js'; +import { miLocalStorage } from '@/local-storage.js'; +import * as os from '@/os.js'; + +const emit = defineEmits<{ + (ev: 'closed'): void; +}>(); + +const zIndex = os.claimZIndex('low'); + +function close() { + miLocalStorage.setItem('modifiedVersionMustProminentlyOfferInAgplV3Section13Read', 'true'); + emit('closed'); +} +</script> + +<style lang="scss" module> +.root { + position: fixed; + z-index: v-bind(zIndex); + bottom: var(--margin); + left: 0; + right: 0; + margin: auto; + box-sizing: border-box; + width: calc(100% - (var(--margin) * 2)); + max-width: 500px; + display: flex; +} + +.icon { + text-align: center; + padding-top: 25px; + width: 100px; + color: var(--accent); +} +@media (max-width: 500px) { + .icon { + width: 80px; + } +} +@media (max-width: 450px) { + .icon { + width: 70px; + } +} + +.main { + padding: 25px 25px 25px 0; + flex: 1; +} + +.close { + position: absolute; + top: 8px; + right: 8px; + padding: 8px; +} + +.title { + font-weight: bold; +} + +.text { + margin: 0.7em 0 1em 0; +} +</style> diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts index 355715bcc6..3de81c9bb9 100644 --- a/packages/frontend/src/local-storage.ts +++ b/packages/frontend/src/local-storage.ts @@ -12,6 +12,7 @@ type Keys = 'latestDonationInfoShownAt' | 'neverShowDonationInfo' | 'neverShowLocalOnlyInfo' | + 'modifiedVersionMustProminentlyOfferInAgplV3Section13Read' | 'lastUsed' | 'lang' | 'drafts' | diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue index a2ecae3b80..fd97ab97b9 100644 --- a/packages/frontend/src/pages/about-misskey.vue +++ b/packages/frontend/src/pages/about-misskey.vue @@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_gaps_s"> <FormLink to="https://github.com/misskey-dev/misskey" external> <template #icon><i class="ti ti-code"></i></template> - {{ i18n.ts._aboutMisskey.source }} + {{ i18n.ts._aboutMisskey.source }} ({{ i18n.ts._aboutMisskey.original }}) <template #suffix>GitHub</template> </FormLink> <FormLink to="https://crowdin.com/project/misskey" external> @@ -46,6 +46,25 @@ SPDX-License-Identifier: AGPL-3.0-only </FormLink> </div> </FormSection> + <FormSection v-if="instance.repositoryUrl !== 'https://github.com/misskey-dev/misskey'"> + <div class="_gaps_s"> + <MkInfo> + {{ i18n.tsx._aboutMisskey.thisIsModifiedVersion({ name: instance.name }) }} + </MkInfo> + <FormLink v-if="instance.repositoryUrl" :to="instance.repositoryUrl" external> + <template #icon><i class="ti ti-code"></i></template> + {{ i18n.ts._aboutMisskey.source }} + </FormLink> + <FormLink v-if="instance.providesTarball" :to="`/tarball/misskey-${version}.tar.gz`" external> + <template #icon><i class="ti ti-download"></i></template> + {{ i18n.ts._aboutMisskey.source }} + <template #suffix>Tarball</template> + </FormLink> + <MkInfo v-if="!instance.repositoryUrl && !instance.providesTarball" warn> + {{ i18n.ts.sourceCodeIsNotYetProvided }} + </MkInfo> + </div> + </FormSection> <FormSection> <template #label>{{ i18n.ts._aboutMisskey.projectMembers }}</template> <div :class="$style.contributors"> @@ -118,9 +137,10 @@ import { version } from '@/config.js'; import FormLink from '@/components/form/link.vue'; import FormSection from '@/components/form/section.vue'; import MkButton from '@/components/MkButton.vue'; -import MkLink from '@/components/MkLink.vue'; +import MkInfo from '@/components/MkInfo.vue'; import { physics } from '@/scripts/physics.js'; import { i18n } from '@/i18n.js'; +import { instance } from '@/instance.js'; import { defaultStore } from '@/store.js'; import * as os from '@/os.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index f99eb9c577..324d1c11de 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -31,7 +31,17 @@ SPDX-License-Identifier: AGPL-3.0-only </MkKeyValue> <div v-html="i18n.tsx.poweredByMisskeyDescription({ name: instance.name ?? host })"> </div> - <FormLink to="/about-misskey">{{ i18n.ts.aboutMisskey }}</FormLink> + <FormLink to="/about-misskey"> + <template #icon><i class="ti ti-info-circle"></i></template> + {{ i18n.ts.aboutMisskey }} + </FormLink> + <FormLink v-if="instance.repositoryUrl || instance.providesTarball" :to="instance.repositoryUrl || `/tarball/misskey-${version}.tar.gz`" external> + <template #icon><i class="ti ti-code"></i></template> + {{ i18n.ts.sourceCode }} + </FormLink> + <MkInfo v-else warn> + {{ i18n.ts.sourceCodeIsNotYetProvided }} + </MkInfo> </div> </FormSection> @@ -47,17 +57,33 @@ SPDX-License-Identifier: AGPL-3.0-only <template #value>{{ instance.maintainerEmail }}</template> </MkKeyValue> </FormSplit> - <FormLink v-if="instance.impressumUrl" :to="instance.impressumUrl" external>{{ i18n.ts.impressum }}</FormLink> + <FormLink v-if="instance.impressumUrl" :to="instance.impressumUrl" external> + <template #icon><i class="ti ti-user-shield"></i></template> + {{ i18n.ts.impressum }} + </FormLink> <div class="_gaps_s"> <MkFolder v-if="instance.serverRules.length > 0"> - <template #label>{{ i18n.ts.serverRules }}</template> + <template #label> + <i class="ti ti-checkup-list"></i> + {{ i18n.ts.serverRules }} + </template> <ol class="_gaps_s" :class="$style.rules"> - <li v-for="item, index in instance.serverRules" :key="index" :class="$style.rule"><div :class="$style.ruleText" v-html="item"></div></li> + <li v-for="(item, index) in instance.serverRules" :key="index" :class="$style.rule"><div :class="$style.ruleText" v-html="item"></div></li> </ol> </MkFolder> - <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.tosUrl" :to="instance.tosUrl" external> + <template #icon><i class="ti ti-license"></i></template> + {{ i18n.ts.termsOfService }} + </FormLink> + <FormLink v-if="instance.privacyPolicyUrl" :to="instance.privacyPolicyUrl" external> + <template #icon><i class="ti ti-shield-lock"></i></template> + {{ i18n.ts.privacyPolicy }} + </FormLink> + <FormLink v-if="instance.feedbackUrl" :to="instance.feedbackUrl" external> + <template #icon><i class="ti ti-message"></i></template> + {{ i18n.ts.feedback }} + </FormLink> </div> </div> </FormSection> @@ -86,7 +112,6 @@ SPDX-License-Identifier: AGPL-3.0-only <FormLink :to="`/.well-known/nodeinfo`" external>nodeinfo</FormLink> <FormLink :to="`/robots.txt`" external>robots.txt</FormLink> <FormLink :to="`/manifest.json`" external>manifest.json</FormLink> - <FormLink :to="`/tarball/misskey-${version}.tar.gz`" external>source code</FormLink> </div> </FormSection> </div> @@ -116,6 +141,7 @@ import FormSuspense from '@/components/form/suspense.vue'; import FormSplit from '@/components/form/split.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; +import MkInfo from '@/components/MkInfo.vue'; import MkInstanceStats from '@/components/MkInstanceStats.vue'; import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; import { misskeyApi } from '@/scripts/misskey-api.js'; diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue index 4ac2011aaf..2b559f92c9 100644 --- a/packages/frontend/src/pages/admin/branding.vue +++ b/packages/frontend/src/pages/admin/branding.vue @@ -76,6 +76,16 @@ SPDX-License-Identifier: AGPL-3.0-only <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template> </MkTextarea> + <MkInput v-model="repositoryUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.repositoryUrl }}</template> + </MkInput> + + <MkInput v-model="feedbackUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.feedbackUrl }}</template> + </MkInput> + <MkTextarea v-model="manifestJsonOverride"> <template #label>{{ i18n.ts._serverSettings.manifestJsonOverride }}</template> </MkTextarea> @@ -120,6 +130,8 @@ const defaultDarkTheme = ref<string | null>(null); const serverErrorImageUrl = ref<string | null>(null); const infoImageUrl = ref<string | null>(null); const notFoundImageUrl = ref<string | null>(null); +const repositoryUrl = ref<string | null>(null); +const feedbackUrl = ref<string | null>(null); const manifestJsonOverride = ref<string>('{}'); async function init() { @@ -135,6 +147,8 @@ async function init() { serverErrorImageUrl.value = meta.serverErrorImageUrl; infoImageUrl.value = meta.infoImageUrl; notFoundImageUrl.value = meta.notFoundImageUrl; + repositoryUrl.value = meta.repositoryUrl; + feedbackUrl.value = meta.feedbackUrl; manifestJsonOverride.value = meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t'); } @@ -151,6 +165,8 @@ function save() { infoImageUrl: infoImageUrl.value === '' ? null : infoImageUrl.value, notFoundImageUrl: notFoundImageUrl.value === '' ? null : notFoundImageUrl.value, serverErrorImageUrl: serverErrorImageUrl.value === '' ? null : serverErrorImageUrl.value, + repositoryUrl: repositoryUrl.value === '' ? null : repositoryUrl.value, + feedbackUrl: feedbackUrl.value === '' ? null : feedbackUrl.value, manifestJsonOverride: manifestJsonOverride.value === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride.value)), }).then(() => { fetchInstance(); diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue index 8af9deae62..c505d70aa9 100644 --- a/packages/frontend/src/pages/admin/settings.vue +++ b/packages/frontend/src/pages/admin/settings.vue @@ -34,6 +34,16 @@ SPDX-License-Identifier: AGPL-3.0-only </MkInput> </FormSplit> + <MkInput v-model="repositoryUrl" type="url"> + <template #label>{{ i18n.ts.repositoryUrl }}</template> + <template #prefix><i class="ti ti-link"></i></template> + <template #caption>{{ i18n.ts.repositoryUrlDescription }}</template> + </MkInput> + + <MkInfo v-if="!instance.providesTarball && !repositoryUrl" warn> + {{ i18n.ts.repositoryUrlOrTarballRequired }} + </MkInfo> + <MkInput v-model="impressumUrl" type="url"> <template #label>{{ i18n.ts.impressumUrl }}</template> <template #prefix><i class="ti ti-link"></i></template> @@ -159,7 +169,7 @@ import FormSplit from '@/components/form/split.vue'; import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { fetchInstance } from '@/instance.js'; +import { fetchInstance, instance } from '@/instance.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkButton from '@/components/MkButton.vue'; @@ -169,6 +179,7 @@ const shortName = ref<string | null>(null); const description = ref<string | null>(null); const maintainerName = ref<string | null>(null); const maintainerEmail = ref<string | null>(null); +const repositoryUrl = ref<string | null>(null); const impressumUrl = ref<string | null>(null); const pinnedUsers = ref<string>(''); const cacheRemoteFiles = ref<boolean>(false); @@ -191,6 +202,7 @@ async function init(): Promise<void> { description.value = meta.description; maintainerName.value = meta.maintainerName; maintainerEmail.value = meta.maintainerEmail; + repositoryUrl.value = meta.repositoryUrl; impressumUrl.value = meta.impressumUrl; pinnedUsers.value = meta.pinnedUsers.join('\n'); cacheRemoteFiles.value = meta.cacheRemoteFiles; @@ -214,6 +226,7 @@ async function save(): void { description: description.value, maintainerName: maintainerName.value, maintainerEmail: maintainerEmail.value, + repositoryUrl: repositoryUrl.value, impressumUrl: impressumUrl.value, pinnedUsers: pinnedUsers.value.split('\n'), cacheRemoteFiles: cacheRemoteFiles.value, |