Compare commits

...

44 commits

Author SHA1 Message Date
Henry Jameson
12b98a48b2 Merge branch 'admin-tabs-2' into shigusegubu-themes3 2025-12-08 22:31:37 +02:00
Henry Jameson
caa0213ac6 changelog 2025-12-08 22:31:05 +02:00
Henry Jameson
a7fa7558b3 pretty much "the rest" 2025-12-08 22:30:27 +02:00
Henry Jameson
bdb992a8e5 lint 2025-12-08 18:17:11 +02:00
Henry Jameson
bc47bef80d branding/manifest part done 2025-12-08 18:12:21 +02:00
Henry Jameson
c4f83808b0 http tab more or less done 2025-12-08 17:09:07 +02:00
Henry Jameson
5aed9a20b8 fix listsetting & http ssl options 2025-12-08 13:50:08 +02:00
Henry Jameson
e961b6e14c small visual fixes 2025-12-08 13:49:30 +02:00
Henry Jameson
df8df5a0bf fix hard-reset not showing 2025-12-07 23:14:30 +02:00
Henry Jameson
7d0f03fdba proxy setting, init http tab, broken 2025-12-07 23:11:54 +02:00
Henry Jameson
0bc5442eb9 third pass of emoji reorganizing 2025-12-07 17:42:10 +02:00
Henry Jameson
4329502422 second pass of reorganizing emoji tab 2025-12-07 17:06:41 +02:00
Henry Jameson
2fe5efc69d first pass of reorganizing emoji tab 2025-12-07 15:43:57 +02:00
Henry Jameson
ace8295c03 style updates 2025-12-07 15:43:52 +02:00
Henry Jameson
d5c75915e6 federation tab 2025-12-07 14:01:33 +02:00
Henry Jameson
d7453c09b2 improvements to listsetting, added some localization strings 2025-12-07 14:01:09 +02:00
Henry Jameson
9c043533f2 MapSetting component 2025-12-06 14:38:53 +02:00
Henry Jameson
b0bce1bf18 proper tuple support 2025-12-06 14:38:39 +02:00
Henry Jameson
48ba3892c3 additions to auth tab 2025-12-04 17:39:57 +02:00
Henry Jameson
ac751320f4 Auth tab done 2025-12-04 17:20:11 +02:00
Henry Jameson
3fca18e248 hide list add string if not in expert mode 2025-12-04 15:47:48 +02:00
Henry Jameson
56a6a25112 Queues tab done 2025-12-04 14:37:25 +02:00
Henry Jameson
cdbf3f42b8 part of job queues tab done 2025-12-03 23:05:46 +02:00
Henry Jameson
0a9a3648d6 registrations tab update 2025-12-03 21:55:53 +02:00
Henry Jameson
5042db43ab add more :instance stuff to attachments tab 2025-12-03 20:40:49 +02:00
Henry Jameson
cae19d8a9a fix links tab, boost description font size 2025-12-03 20:28:39 +02:00
Henry Jameson
311e9d255d media proxy done + misc changes 2025-12-03 20:16:33 +02:00
Henry Jameson
3a321ca756 media proxy completeness 2025-12-03 12:31:57 +02:00
Henry Jameson
42a5da93ea links tab done (finally) 2025-12-03 12:31:57 +02:00
Henry Jameson
b7a97b8603 merge multicheckbox and list inputs 2025-12-02 20:48:13 +02:00
Henry Jameson
c93f55e8f7 list and multicheckbox initial implementation 2025-12-01 23:56:49 +02:00
Henry Jameson
4a98ec9611 minor fixes and moves 2025-12-01 20:14:01 +02:00
Henry Jameson
388ecd9a5e Merge remote-tracking branch 'origin/develop' into admin-tabs-2 2025-12-01 18:52:04 +02:00
HJ
0252d39c75 Merge branch 'settings-shuffle' into 'develop'
Settings shuffle

See merge request pleroma/pleroma-fe!2186
2025-12-01 09:01:46 +00:00
Henry Jameson
235e6bd233 Media proxy tab 2025-11-28 15:44:22 +02:00
Henry Jameson
672bedaf6d fix disabled classes 2025-11-28 15:44:03 +02:00
Henry Jameson
55b5d2c5d7 fixup! uploads tab 2025-11-28 14:00:00 +02:00
Henry Jameson
b73c9ae4e8 frontends tab proper hierarchy 2025-11-28 13:59:13 +02:00
Henry Jameson
114d49b6d6 i18n 2025-11-28 02:11:28 +02:00
Henry Jameson
bc92f535de uploads tab 2025-11-28 02:02:39 +02:00
Henry Jameson
db535ae057 cleanup 2025-11-28 02:02:29 +02:00
Henry Jameson
eae09226b5 registrations tab 2025-11-27 20:28:58 +02:00
Henry Jameson
489fb17070 Merge branch 'settings-shuffle' into admin-tabs-2 2025-11-27 18:20:58 +02:00
Henry Jameson
b38343705c mailer tab + beginning of monitoring tab 2025-11-27 18:18:03 +02:00
71 changed files with 4150 additions and 540 deletions

View file

@ -0,0 +1 @@
Most of the remaining AdminFE tabs into Admin Dashboard

View file

@ -513,6 +513,12 @@ nav {
}
}
label {
&.-disabled {
color: var(--textFaint);
}
}
input,
textarea {
border: none;
@ -553,6 +559,10 @@ textarea {
&[disabled="disabled"],
&.disabled {
cursor: not-allowed;
color: var(--textFaint);
/* stylelint-disable-next-line declaration-no-important */
background-color: transparent !important;
}
&[type="range"] {
@ -578,6 +588,8 @@ textarea {
& + label::before {
opacity: 0.5;
}
background-color: var(--background);
}
+ label::before {
@ -677,7 +689,8 @@ option {
list-style: none;
display: grid;
grid-auto-flow: row dense;
grid-template-columns: 1fr 1fr;
grid-template-columns: repeat(auto-fit, minmax(20em, 1fr));
grid-gap: 0.5em;
li {
border: 1px solid var(--border);
@ -692,13 +705,16 @@ option {
display: inline-flex;
vertical-align: middle;
.Select select {
line-height: 1;
}
> *,
> * .button-default {
--_roundness-left: 0;
--_roundness-right: 0;
position: relative;
flex: 1 1 auto;
}
> *:first-child,

View file

@ -1,7 +1,7 @@
<template>
<label
class="checkbox"
:class="[{ disabled, indeterminate, 'indeterminate-fix': indeterminateTransitionFix }, radio ? '-radio' : '-checkbox']"
:class="[{ ['-disabled']: disabled, indeterminate, 'indeterminate-fix': indeterminateTransitionFix }, radio ? '-radio' : '-checkbox']"
>
<span
v-if="!!$slots.before"
@ -123,7 +123,7 @@ export default {
.disabled {
.checkbox-indicator::before {
background-color: var(--background);
background-color: transparent;
}
}

View file

@ -395,7 +395,7 @@
}
form textarea {
line-height: 16px;
line-height: 1;
resize: vertical;
}

View file

@ -26,6 +26,7 @@
/* TODO fix order of styles */
label.Select {
padding: 0;
display: flex;
select {
appearance: none;
@ -33,13 +34,12 @@ label.Select {
border: none;
color: var(--text);
margin: 0;
padding: 0 2em 0 0.2em;
padding: 0 2em 0 0.5em;
font-family: var(--font);
font-size: 1em;
width: 100%;
z-index: 1;
height: 2em;
line-height: 16px;
line-height: 2;
&[multiple],
&[size] {
@ -79,7 +79,7 @@ label.Select {
position: absolute;
top: 0;
bottom: 0;
right: 5px;
right: 0.5em;
height: 100%;
width: 0.875em;
font-family: var(--font);

View file

@ -0,0 +1,39 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import TupleSetting from '../helpers/tuple_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import MapSetting from '../helpers/map_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
const AuthTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting,
TupleSetting,
AttachmentSetting,
GroupSetting,
ListSetting,
MapSetting
},
computed: {
...SharedComputedObject(),
LDAPEnabled () {
return this.$store.state.adminSettings.draft[':pleroma'][':ldap'][':enabled']
},
}
}
export default AuthTab

View file

@ -0,0 +1,105 @@
<template>
<div :label="$t('admin_dash.tabs.job_queues')">
<div class="setting-item">
<h3>{{ $t('admin_dash.auth.MFA') }}</h3>
<ul class="setting-list">
<li>
<h4>{{ $t('admin_dash.auth.TOTP') }}</h4>
<ul class="setting-list suboptions">
<li>
<IntegerSetting path=":pleroma.:instance.:multi_factor_authentication.:totp.:digits" />
</li>
<li>
<IntegerSetting path=":pleroma.:instance.:multi_factor_authentication.:totp.:period" />
</li>
</ul>
</li>
<li>
<h4>{{ $t('admin_dash.auth.backup_codes') }}</h4>
<ul class="setting-list suboptions">
<li>
<IntegerSetting path=":pleroma.:instance.:multi_factor_authentication.:backup_codes.:number" />
</li>
<li>
<IntegerSetting path=":pleroma.:instance.:multi_factor_authentication.:backup_codes.:length" />
</li>
</ul>
</li>
<GroupSetting path=":pleroma.:instance.:multi_factor_authentication" />
</ul>
<h3>{{ $t('admin_dash.auth.OAuth') }}</h3>
<ul class="setting-list">
<li>
<StringSetting path=":pleroma.:auth.:auth_template" />
</li>
<li>
<BooleanSetting path=":pleroma.:auth.:enforce_oauth_admin_scope_usage" />
</li>
<li>
<StringSetting path=":pleroma.:auth.:oauth_consumer_template" />
</li>
<li>
<ListSetting path=":pleroma.:auth.:oauth_consumer_strategies" />
</li>
<li>
<IntegerSetting path=":pleroma.:oauth2.:token_expires_in" />
</li>
<li>
<BooleanSetting path=":pleroma.:oauth2.:issue_new_refresh_token" />
</li>
<li>
<BooleanSetting path=":pleroma.:oauth2.:clean_expired_tokens" />
</li>
</ul>
<h3>{{ $t('admin_dash.auth.LDAP') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:ldap.:enabled" />
</li>
<template v-if="LDAPEnabled">
<li>
<StringSetting path=":pleroma.:ldap.:host" />
</li>
<li>
<IntegerSetting path=":pleroma.:ldap.:port" />
</li>
<li>
<BooleanSetting path=":pleroma.:ldap.:tls" />
</li>
<li>
<!-- CONFIRM old admin FE only supports ONE setting which is Verify, is that correct or should we allow more than one? -->
<MapSetting
:allow-new="false"
path=":pleroma.:ldap.:tlsopts"
/>
</li>
<li>
<BooleanSetting path=":pleroma.:ldap.:ssl" />
</li>
<li>
<!-- CONFIRM old admin FE only supports ONE setting which is Verify, is that correct or should we allow more than one? -->
<MapSetting
:allow-new="false"
path=":pleroma.:ldap.:sslopts"
/>
</li>
<li>
<StringSetting path=":pleroma.:ldap.:base" />
</li>
<li>
<StringSetting path=":pleroma.:ldap.:uid" />
</li>
<li>
<StringSetting path=":pleroma.:ldap.:cacertfile" />
</li>
<li>
<StringSetting path=":pleroma.:ldap.:mail" />
</li>
</template>
</ul>
</div>
<!-- CONFIRM admin token is present in AdminFE but missing in both data and descriptions?? -->
</div>
</template>
<script src="./auth_tab.js"></script>

View file

@ -10,6 +10,22 @@ import ModifiedIndicator from '../helpers/modified_indicator.vue'
import EmojiEditingPopover from '../helpers/emoji_editing_popover.vue'
import { useInterfaceStore } from 'src/stores/interface'
import SharedComputedObject from '../helpers/shared_computed_object.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faArrowsRotate,
faFolderOpen,
faServer,
faDownload
} from '@fortawesome/free-solid-svg-icons'
library.add(
faArrowsRotate,
faFolderOpen,
faDownload,
faServer
)
const EmojiTab = {
components: {
TabSwitcher,
@ -44,10 +60,12 @@ const EmojiTab = {
},
computed: {
...SharedComputedObject(),
pack () {
return this.packName !== '' ? this.knownPacks[this.packName] : undefined
},
packMeta () {
if (this.packName === '') return {}
if (this.editedMetadata[this.packName] === undefined) {
this.editedMetadata[this.packName] = clone(this.pack.pack)
}
@ -98,8 +116,6 @@ const EmojiTab = {
return Promise.reject(resp)
}
}).then(() => {
this.$refs.createPackPopover.hidePopover()
this.packName = this.newPackName
this.newPackName = ''
})
@ -205,8 +221,6 @@ const EmojiTab = {
for (const pack in this.knownRemotePacks[inst]) {
this.sortPackFiles(`${pack}@${inst}`)
}
this.$refs.remotePackPopover.hidePopover()
})
.catch(data => {
this.displayError(data)
@ -223,8 +237,6 @@ const EmojiTab = {
.then(data => data.json())
.then(resp => {
if (resp === 'ok') {
this.$refs.downloadPackPopover.hidePopover()
return this.refreshPackList()
} else {
this.displayError(resp.error)
@ -242,8 +254,6 @@ const EmojiTab = {
.then(data => data.json())
.then(resp => {
if (resp === 'ok') {
this.$refs.additionalRemotePopover.hidePopover()
return this.refreshPackList()
} else {
this.displayError(resp.error)
@ -262,8 +272,6 @@ const EmojiTab = {
.then(data => data.json())
.then(resp => {
if (resp === 'ok') {
this.$refs.additionalRemotePopover.hidePopover()
return this.refreshPackList()
} else {
this.displayError(resp.error)

View file

@ -1,10 +1,48 @@
.emoji-tab {
.btn-group .btn:not(:first-child) {
margin-left: 0.5em;
.EmojiTab {
.setting-list {
padding-left: 0.75em;
}
.pack-info-wrapper {
.toolbar {
display: flex;
flex-wrap: wrap;
gap: 0.5em;
.header-buttons {
display: flex;
flex: 1 0 auto;
justify-content: end;
&:not(.btn-group) {
gap: 0.5em;
}
.header-text {
flex: 1 0 auto;
}
}
.button-default {
flex: 0 0 auto;
padding: 0.5em;
font-size: 1rem;
}
}
.selector-buttons,
.meta-buttons {
display: flex;
flex-wrap: wrap;
gap: 0.5em;
}
h5 {
margin-top: 1em;
margin-bottom: 0.25em;
}
h3.toolbar {
align-items: end;
}
.emoji-info-input {
@ -18,8 +56,8 @@
}
.emoji {
width: 32px;
height: 32px;
width: 2em;
height: 2em;
}
.emoji-unsaved {
@ -30,6 +68,14 @@
display: flex;
flex-wrap: wrap;
gap: 1em;
.placeholder {
background: var(--textFaint);
border-radius: 0.5em;
width: 2em;
height: 2em;
opacity: 0.5;
}
}
}

View file

@ -1,168 +1,75 @@
<template>
<div
class="emoji-tab"
class="EmojiTab"
:label="$t('admin_dash.tabs.emoji')"
>
<div class="setting-item">
<h2>{{ $t('admin_dash.tabs.emoji') }}</h2>
<h3 class="toolbar">
<span class="header-text">
{{ $t('admin_dash.emoji.emoji_packs') }}
</span>
<ul class="setting-list">
<h3>{{ $t('admin_dash.emoji.global_actions') }}</h3>
<li class="btn-group setting-item">
<span class="header-buttons btn-group">
<button
class="button button-default btn"
class="button button-default"
type="button"
:title="$t('admin_dash.emoji.reload')"
@click="reloadEmoji"
>
{{ $t('admin_dash.emoji.reload') }}
</button>
<button
class="button button-default btn"
type="button"
@click="importFromFS"
>
{{ $t('admin_dash.emoji.importFS') }}
</button>
</li>
<li class="btn-group setting-item">
<button
class="button button-default btn"
type="button"
@click="$refs.remotePackPopover.showPopover"
>
{{ $t('admin_dash.emoji.remote_packs') }}
<Popover
ref="remotePackPopover"
popover-class="emoji-tab-edit-popover popover-default"
trigger="click"
placement="bottom"
bound-to-selector=".emoji-tab"
:bound-to="{ x: 'container' }"
:offset="{ y: 5 }"
>
<template #content>
<div class="emoji-tab-popover-input">
<h3>{{ $t('admin_dash.emoji.remote_pack_instance') }}</h3>
<input
v-model="remotePackInstance"
class="input"
:placeholder="$t('admin_dash.emoji.remote_pack_instance')"
>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
@click="listRemotePacks"
>
{{ $t('admin_dash.emoji.do_list') }}
</button>
</div>
</template>
</Popover>
</button>
<button
class="button button-default emoji-panel-additional-actions"
@click="$refs.additionalRemotePopover.showPopover"
>
<FAIcon
icon="chevron-down"
/>
<Popover
ref="additionalRemotePopover"
popover-class="emoji-tab-edit-popover popover-default"
trigger="click"
placement="bottom"
bound-to-selector=".emoji-tab"
:bound-to="{ x: 'container' }"
:offset="{ y: 5 }"
>
<template #content>
<div class="emoji-tab-popover-input">
<h3>{{ $t('admin_dash.emoji.new_pack_name') }}</h3>
<input
v-model="newPackName"
:placeholder="$t('admin_dash.emoji.new_pack_name')"
class="input"
>
<h3>Import pack from URL</h3>
<input
v-model="remotePackURL"
class="input"
placeholder="Pack .zip URL"
>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
:disabled="newPackName.trim() === '' || remotePackURL.trim() === ''"
@click="downloadRemoteURLPack"
>
Import
</button>
<h3>Import pack from a file</h3>
<input
type="file"
accept="application/zip"
class="emoji-tab-popover-file input"
@change="remotePackFile = $event.target.files"
>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
:disabled="newPackName.trim() === '' || remotePackFile === null || remotePackFile.length === 0"
@click="downloadRemoteFilePack"
>
Import
</button>
</div>
</template>
</Popover>
</button>
</li>
<h3>{{ $t('admin_dash.emoji.emoji_packs') }}</h3>
<li>
<h4>{{ $t('admin_dash.emoji.edit_pack') }}</h4>
<Select
v-model="packName"
class="form-control"
>
<option
value=""
disabled
hidden
>
{{ $t('admin_dash.emoji.emoji_pack') }}
</option>
<option
v-for="(pack, listPackName) in knownPacks"
:key="listPackName"
:label="listPackName"
>
{{ listPackName }}
</option>
</Select>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
@click="$refs.createPackPopover.showPopover"
>
{{ $t('admin_dash.emoji.create_pack') }}
<FAIcon icon="arrows-rotate" />
{{ $t('admin_dash.emoji.reload_short') }}
</button>
<Popover
ref="createPackPopover"
popover-class="emoji-tab-edit-popover popover-default"
trigger="click"
placement="bottom"
bound-to-selector=".emoji-tab"
:bound-to="{ x: 'container' }"
:offset="{ y: 5 }"
>
<template #trigger>
<button
class="button button-default"
type="button"
:title="$t('admin_dash.emoji.remote_packs')"
>
<FAIcon icon="download" />
{{ $t('admin_dash.emoji.remote_packs_short') }}
</button>
</template>
<template #content>
<div class="emoji-tab-popover-input">
<h3>{{ $t('admin_dash.emoji.remote_pack_instance') }}</h3>
<input
v-model="remotePackInstance"
class="input"
:placeholder="$t('admin_dash.emoji.remote_pack_instance')"
>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
@click="listRemotePacks"
>
{{ $t('admin_dash.emoji.do_list') }}
</button>
</div>
</template>
</Popover>
<Popover
ref="additionalRemotePopover"
popover-class="emoji-tab-edit-popover popover-default"
trigger="click"
placement="bottom"
>
<template #trigger>
<button
class="button button-default emoji-panel-additional-actions"
:title="$t('admin_dash.emoji.import_pack')"
@click="$refs.additionalRemotePopover.showPopover"
>
<FAIcon icon="folder-open" />
{{ $t('admin_dash.emoji.import_pack_short') }}
</button>
</template>
<template #content>
<div class="emoji-tab-popover-input">
<h3>{{ $t('admin_dash.emoji.new_pack_name') }}</h3>
@ -171,94 +78,252 @@
:placeholder="$t('admin_dash.emoji.new_pack_name')"
class="input"
>
<h3>Import pack from URL</h3>
<input
v-model="remotePackURL"
class="input"
placeholder="Pack .zip URL"
>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
@click="createEmojiPack"
:disabled="newPackName.trim() === '' || remotePackURL.trim() === ''"
@click="downloadRemoteURLPack"
>
{{ $t('admin_dash.emoji.create') }}
Import
</button>
<h3>Import pack from a file</h3>
<input
type="file"
accept="application/zip"
class="emoji-tab-popover-file input"
@change="remotePackFile = $event.target.files"
>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
:disabled="newPackName.trim() === '' || remotePackFile === null || remotePackFile.length === 0"
@click="downloadRemoteFilePack"
>
Import
</button>
</div>
</template>
</Popover>
</li>
</ul>
</span>
</h3>
<div class="setting-list">
<h4 class="toolbar">
{{ $t('admin_dash.emoji.edit_pack') }}
</h4>
<div class="selector-buttons setting-list">
<button
:disabled="!pack || pack.remote !== undefined"
class="button button-default btn"
type="button"
@click="deleteModalVisible = true"
>
{{ $t('admin_dash.emoji.delete_pack') }}
<div v-if="pack">
<div class="pack-info-wrapper">
<ul class="setting-list">
<li>
<label>
{{ $t('admin_dash.emoji.description') }}
<ModifiedIndicator
:changed="metaEdited('description')"
message-key="admin_dash.emoji.metadata_changed"
/>
<ConfirmModal
v-if="deleteModalVisible"
:title="$t('admin_dash.emoji.delete_title')"
:cancel-text="$t('status.delete_confirm_cancel_button')"
:confirm-text="$t('status.delete_confirm_accept_button')"
@cancelled="deleteModalVisible = false"
@accepted="deleteEmojiPack"
>
{{ $t('admin_dash.emoji.delete_confirm', [packName]) }}
</ConfirmModal>
</button>
<textarea
v-model="packMeta.description"
:disabled="pack.remote !== undefined"
class="bio resize-height input"
/>
</label>
</li>
<li>
<label>
{{ $t('admin_dash.emoji.homepage') }}
<ModifiedIndicator
:changed="metaEdited('homepage')"
message-key="admin_dash.emoji.metadata_changed"
/>
<button
:disabled="!pack || pack.remote === undefined"
class="button button-default btn"
type="button"
@click="$refs.downloadPackPopover.showPopover"
>
{{ $t('admin_dash.emoji.download_pack') }}
<input
v-model="packMeta.homepage"
class="emoji-info-input input"
:disabled="pack.remote !== undefined"
>
</label>
</li>
<li>
<label>
{{ $t('admin_dash.emoji.fallback_src') }}
<ModifiedIndicator
:changed="metaEdited('fallback-src')"
message-key="admin_dash.emoji.metadata_changed"
/>
<Popover
ref="downloadPackPopover"
trigger="click"
placement="bottom"
bound-to-selector=".emoji-tab"
popover-class="emoji-tab-edit-popover popover-default"
:bound-to="{ x: 'container' }"
:offset="{ y: 5 }"
>
<template #content>
<h3>{{ $t('admin_dash.emoji.downloading_pack', [packName]) }}</h3>
<div>
<div>
<div class="emoji-tab-popover-input">
<label>
{{ $t('admin_dash.emoji.download_as_name') }}
<input
v-model="remotePackDownloadAs"
class="emoji-data-input input"
:placeholder="$t('admin_dash.emoji.download_as_name_full')"
>
</label>
<input
v-model="packMeta['fallback-src']"
class="emoji-info-input input"
:disabled="pack.remote !== undefined"
>
</label>
</li>
<li>
<label>
{{ $t('admin_dash.emoji.fallback_sha256') }}
<div
v-if="downloadWillReplaceLocal"
class="warning"
>
<em>{{ $t('admin_dash.emoji.replace_warning') }}</em>
</div>
</div>
<input
v-model="packMeta['fallback-src-sha256']"
:disabled="true"
class="emoji-info-input input"
>
</label>
</li>
<li>
<Checkbox
v-model="packMeta['share-files']"
:disabled="pack.remote !== undefined"
<button
class="button button-default btn"
type="button"
@click="downloadRemotePack"
>
{{ $t('admin_dash.emoji.download') }}
</button>
</div>
</div>
</template>
</Popover>
</button>
<span class="btn-group">
<Select
v-model="packName"
class="form-control"
>
<option
value=""
disabled
hidden
>
{{ $t('admin_dash.emoji.share') }}
</Checkbox>
{{ $t('admin_dash.emoji.emoji_pack') }}
</option>
<option
v-for="(pack, listPackName) in knownPacks"
:key="listPackName"
:label="listPackName"
>
{{ listPackName }}
</option>
</Select>
<Popover
ref="createPackPopover"
popover-class="emoji-tab-edit-popover popover-default"
trigger="click"
placement="bottom"
>
<template #trigger>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
>
{{ $t('admin_dash.emoji.create_pack') }}
</button>
</template>
<template #content>
<div class="emoji-tab-popover-input">
<h3>{{ $t('admin_dash.emoji.new_pack_name') }}</h3>
<input
v-model="newPackName"
:placeholder="$t('admin_dash.emoji.new_pack_name')"
class="input"
>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
@click="createEmojiPack"
>
{{ $t('admin_dash.emoji.create') }}
</button>
</div>
</template>
</Popover>
</span>
</div>
<h5>
{{ $t('admin_dash.emoji.metadata') }}
<ModifiedIndicator
:changed="$refs.emojiPopovers && $refs.emojiPopovers.some(p => p.isEdited)"
message-key="admin_dash.emoji.emoji_changed"
/>
</h5>
<ul class="setting-list">
<li>
<label :class="{ ['-disabled']: !pack || pack.remote !== undefined }">
{{ $t('admin_dash.emoji.description') }}
<ModifiedIndicator
:changed="metaEdited('share-files')"
:changed="metaEdited('description')"
message-key="admin_dash.emoji.metadata_changed"
/>
</li>
<li class="btn-group">
<textarea
v-model="packMeta.description"
:disabled="!pack || pack.remote !== undefined"
class="bio resize-height input"
/>
</label>
</li>
<li>
<label :class="{ ['-disabled']: !pack || pack.remote !== undefined }">
{{ $t('admin_dash.emoji.homepage') }}
<ModifiedIndicator
:changed="metaEdited('homepage')"
message-key="admin_dash.emoji.metadata_changed"
/>
<input
v-model="packMeta.homepage"
class="emoji-info-input input"
:disabled="!pack || pack.remote !== undefined"
>
</label>
</li>
<li>
<label :class="{ ['-disabled']: !pack || pack.remote !== undefined }">
{{ $t('admin_dash.emoji.fallback_src') }}
<ModifiedIndicator
:changed="metaEdited('fallback-src')"
message-key="admin_dash.emoji.metadata_changed"
/>
<input
v-model="packMeta['fallback-src']"
class="emoji-info-input input"
:disabled="!pack || pack.remote !== undefined"
>
</label>
</li>
<li>
<label :class="{ ['-disabled']: !pack || pack.remote !== undefined }">
{{ $t('admin_dash.emoji.fallback_sha256') }}
<input
v-model="packMeta['fallback-src-sha256']"
:disabled="!pack || pack.remote !== undefined"
class="emoji-info-input input"
>
</label>
</li>
<li>
<Checkbox
v-model="packMeta['share-files']"
:disabled="!pack || pack.remote !== undefined"
>
{{ $t('admin_dash.emoji.share') }}
</Checkbox>
<ModifiedIndicator
:changed="metaEdited('share-files')"
message-key="admin_dash.emoji.metadata_changed"
/>
<div class="meta-buttons">
<button
v-if="pack.remote === undefined"
v-if="pack && pack.remote === undefined"
class="button button-default btn"
type="button"
@click="savePackMetadata"
@ -266,148 +331,88 @@
{{ $t('admin_dash.emoji.save_meta') }}
</button>
<button
v-if="pack.remote === undefined"
v-if="pack && pack.remote === undefined"
class="button button-default btn"
type="button"
@click="savePackMetadata"
>
{{ $t('admin_dash.emoji.revert_meta') }}
</button>
<button
v-if="pack.remote === undefined"
class="button button-default btn"
type="button"
@click="deleteModalVisible = true"
>
{{ $t('admin_dash.emoji.delete_pack') }}
<ConfirmModal
v-if="deleteModalVisible"
:title="$t('admin_dash.emoji.delete_title')"
:cancel-text="$t('status.delete_confirm_cancel_button')"
:confirm-text="$t('status.delete_confirm_accept_button')"
@cancelled="deleteModalVisible = false"
@accepted="deleteEmojiPack"
>
{{ $t('admin_dash.emoji.delete_confirm', [packName]) }}
</ConfirmModal>
</button>
<button
v-if="pack.remote !== undefined"
class="button button-default btn"
type="button"
@click="$refs.downloadPackPopover.showPopover"
>
{{ $t('admin_dash.emoji.download_pack') }}
<Popover
ref="downloadPackPopover"
trigger="click"
placement="bottom"
bound-to-selector=".emoji-tab"
popover-class="emoji-tab-edit-popover popover-default"
:bound-to="{ x: 'container' }"
:offset="{ y: 5 }"
>
<template #content>
<h3>{{ $t('admin_dash.emoji.downloading_pack', [packName]) }}</h3>
<div>
<div>
<div class="emoji-tab-popover-input">
<label>
{{ $t('admin_dash.emoji.download_as_name') }}
<input
v-model="remotePackDownloadAs"
class="emoji-data-input input"
:placeholder="$t('admin_dash.emoji.download_as_name_full')"
>
</label>
<div
v-if="downloadWillReplaceLocal"
class="warning"
>
<em>{{ $t('admin_dash.emoji.replace_warning') }}</em>
</div>
</div>
<button
class="button button-default btn"
type="button"
@click="downloadRemotePack"
>
{{ $t('admin_dash.emoji.download') }}
</button>
</div>
</div>
</template>
</Popover>
</button>
</li>
</ul>
</div>
<ul class="setting-list">
<h4>
{{ $t('admin_dash.emoji.files') }}
<ModifiedIndicator
v-if="pack"
:changed="$refs.emojiPopovers && $refs.emojiPopovers.some(p => p.isEdited)"
message-key="admin_dash.emoji.emoji_changed"
/>
</h4>
<div
v-if="pack"
class="emoji-list"
>
<EmojiEditingPopover
v-if="pack.remote === undefined"
placement="bottom"
new-upload
:title="$t('admin_dash.emoji.adding_new')"
:pack-name="packName"
@update-pack-files="updatePackFiles"
@display-error="displayError"
>
<template #trigger>
<FAIcon
icon="plus"
size="2x"
:title="$t('admin_dash.emoji.add_file')"
/>
</template>
</EmojiEditingPopover>
<EmojiEditingPopover
v-for="(file, shortcode) in pack.files"
ref="emojiPopovers"
:key="shortcode"
placement="top"
:title="$t(`admin_dash.emoji.${pack.remote === undefined ? 'editing' : 'copying'}`, [shortcode])"
:shortcode="shortcode"
:file="file"
:pack-name="packName"
:remote="pack.remote"
:known-local-packs="knownLocalPacks"
@update-pack-files="updatePackFiles"
@display-error="displayError"
>
<template #trigger>
<StillImage
class="emoji"
:src="emojiAddr(file)"
:title="`:${shortcode}:`"
:alt="`:${shortcode}:`"
/>
</template>
</EmojiEditingPopover>
</div>
</div>
</li>
</ul>
<h5>
{{ $t('admin_dash.emoji.files') }}
<ModifiedIndicator
:changed="$refs.emojiPopovers && $refs.emojiPopovers.some(p => p.isEdited)"
message-key="admin_dash.emoji.emoji_changed"
/>
</h5>
<div
class="emoji-list setting-list"
>
<EmojiEditingPopover
v-if="pack && pack.remote === undefined"
placement="bottom"
new-upload
:title="$t('admin_dash.emoji.adding_new')"
:pack-name="packName"
@update-pack-files="updatePackFiles"
@display-error="displayError"
>
<template #trigger>
<FAIcon
icon="plus"
size="2x"
:title="$t('admin_dash.emoji.add_file')"
/>
</template>
</EmojiEditingPopover>
<template v-if="!pack">
<div
v-for="(_, i) in new Array(20)"
:key="i"
class="placeholder"
/>
</template>
<EmojiEditingPopover
v-for="(file, shortcode) in (pack?.files || [])"
ref="emojiPopovers"
:key="shortcode"
placement="top"
:title="$t(`admin_dash.emoji.${pack?.remote === undefined ? 'editing' : 'copying'}`, [shortcode])"
:shortcode="shortcode"
:file="file"
:pack-name="packName"
:remote="pack?.remote"
:known-local-packs="knownLocalPacks"
@update-pack-files="updatePackFiles"
@display-error="displayError"
>
<template #trigger>
<StillImage
class="emoji"
:src="emojiAddr(file)"
:title="`:${shortcode}:`"
:alt="`:${shortcode}:`"
/>
</template>
</EmojiEditingPopover>
</div>
</div>
<h3>{{ $t('admin_dash.emoji.advanced') }}</h3>
<button
class="button button-default btn"
type="button"
@click="importFromFS"
>
<FAIcon icon="server" />
{{ $t('admin_dash.emoji.importFS') }}
</button>
</div>
</div>
</template>

View file

@ -0,0 +1,34 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import ListTupleSetting from '../helpers/list_tuple_setting.vue'
import MapSetting from '../helpers/map_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
const FederationTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
IntegerSetting,
StringSetting,
AttachmentSetting,
ListSetting,
ListTupleSetting,
GroupSetting,
MapSetting
},
computed: {
...SharedComputedObject()
}
}
export default FederationTab

View file

@ -0,0 +1,68 @@
<template>
<div :label="$t('admin_dash.tabs.federation')">
<div class="setting-item">
<h3>{{ $t('admin_dash.federation.global') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:instance.:federating" />
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:skip_thread_containment" />
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:external_user_synchronization" />
</li>
</ul>
<h3>{{ $t('admin_dash.federation.restrictions') }}</h3>
<ul class="setting-list">
<li>
<MapSetting path=":pleroma.:instance.:quarantined_instances" />
</li>
<li>
<MapSetting path=":pleroma.:instance.:rejected_instances" />
</li>
</ul>
<h3>{{ $t('admin_dash.federation.activitypub') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:instance.:allow_relay" />
</li>
<li>
<BooleanSetting path=":pleroma.:activitypub.:unfollow_blocked" />
</li>
<li>
<BooleanSetting path=":pleroma.:activitypub.:outgoing_blocks" />
</li>
<li>
<BooleanSetting path=":pleroma.:activitypub.:blockers_visible" />
</li>
<li>
<IntegerSetting path=":pleroma.:activitypub.:follow_handshake_timeout" />
</li>
<li>
<IntegerSetting path=":pleroma.:activitypub.:note_replies_output_limit" />
</li>
<li>
<IntegerSetting path=":pleroma.:instance.:federation_incoming_replies_max_depth" />
</li>
<li>
<IntegerSetting path=":pleroma.:instance.:federation_reachability_timeout_days" />
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:allow_relay" />
</li>
<li>
<BooleanSetting path=":pleroma.:activitypub.:sign_object_fetches" />
</li>
<li>
<BooleanSetting path=":pleroma.:activitypub.:authorized_fetch_mode" />
</li>
<li>
<BooleanSetting path=":pleroma.:activitypub.:client_api_enabled" />
</li>
</ul>
</div>
</div>
</template>
<script src="./federation_tab.js"></script>

View file

@ -44,10 +44,10 @@ const FrontendsTab = {
}
},
computed: {
...SharedComputedObject(),
frontends () {
return this.$store.state.adminSettings.frontends
},
...SharedComputedObject()
}
},
methods: {
canInstall (frontend) {

View file

@ -1,8 +1,24 @@
.frontends-tab {
.FrontendsTab {
.cards-list {
padding: 0;
}
li.frontend-card {
display: flex;
margin: 0;
flex-direction: column;
}
.frontend-buttons {
margin-top: 0.5em;
display: flex;
justify-content: end;
gap: 0.5em;
flex-wrap: wrap;
flex: 1 0 auto;
align-items: end;
}
.relative {
position: relative;
}
@ -16,10 +32,26 @@
inset: 0;
}
h5 {
margin: 0;
font-size: 1.15em
}
dl {
margin-left: 1em;
}
dt {
margin-top: 0.5em;
text-overflow: ellipsis;
white-space: nowrap;
overflow-x: hidden;
}
dd {
text-overflow: ellipsis;
white-space: nowrap;
overflow-x: hidden;
max-width: 10em;
}
}

View file

@ -1,17 +1,17 @@
<template>
<div
class="frontends-tab"
class="FrontendsTab"
:label="$t('admin_dash.tabs.frontends')"
>
<div class="setting-item">
<h2>{{ $t('admin_dash.tabs.frontends') }}</h2>
<h3>{{ $t('admin_dash.frontend.title') }}</h3>
<p>{{ $t('admin_dash.frontend.wip_notice') }}</p>
<ul
v-if="adminDraft"
class="setting-list"
>
<li>
<h3>{{ $t('admin_dash.frontend.default_frontend') }}</h3>
<h4>{{ $t('admin_dash.frontend.default_frontend') }}</h4>
<p>{{ $t('admin_dash.frontend.default_frontend_tip') }}</p>
<ul class="setting-list">
<li>
@ -38,13 +38,14 @@
v-if="working"
class="overlay"
/>
<h3>{{ $t('admin_dash.frontend.available_frontends') }}</h3>
<h4>{{ $t('admin_dash.frontend.available_frontends') }}</h4>
<ul class="cards-list">
<li
v-for="frontend in frontends"
:key="frontend.name"
class="frontend-card"
>
<strong>{{ frontend.name }}</strong>
<h5>{{ frontend.name }}</h5>
{{ ' ' }}
<span v-if="adminDraft && adminDraft[':pleroma'][':frontends'][':primary']?.name === frontend.name">
<i18n-t
@ -89,7 +90,7 @@
>{{ frontend.build_url }}</a>
</dd>
</dl>
<div>
<div class="frontend-buttons">
<span class="btn-group">
<button
class="button button-default btn"

View file

@ -0,0 +1,46 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import TupleSetting from '../helpers/tuple_setting.vue'
import MapSetting from '../helpers/map_setting.vue'
import ProxySetting from '../helpers/proxy_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
import { get } from 'lodash'
const HTTPTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting,
AttachmentSetting,
MapSetting,
GroupSetting,
ListSetting,
TupleSetting,
ProxySetting
},
computed: {
...SharedComputedObject(),
sslOptions () {
const desc = get(this.$store.state.adminSettings.descriptions, ':pleroma.:http.:adapter.:ssl_options.:versions')
return new Set(desc.suggestions.map(option => ({
label: option.replace(':tlsv', 'TLS v'),
value: option
})))
},
}
}
export default HTTPTab

View file

@ -0,0 +1,126 @@
<template>
<div
class="LinksTab"
:label="$t('admin_dash.tabs.http')"
>
<div class="setting-item">
<h3>{{ $t('admin_dash.http.outbound') }}</h3>
<ul class="setting-list">
<li>
<ProxySetting
hide-description
path=":pleroma.:http.:proxy_url"
/>
</li>
<li>
<BooleanSetting path=":pleroma.:http.:send_user_agent" />
</li>
<li>
<StringSetting path=":pleroma.:http.:user_agent" />
</li>
<li>
<ListSetting
override-available-options
:options="sslOptions"
path=":pleroma.:http.:adapter.:ssl_options.:versions"
/>
</li>
<li>
<GroupSetting path=":pleroma.:http.:adapter" />
</li>
</ul>
</div>
<div class="setting-item">
<h3>{{ $t('admin_dash.http.incoming') }}</h3>
<ul class="setting-list">
<h4>{{ $t('admin_dash.http.cors') }}</h4>
<li>
<IntegerSetting
:description-path-override="[':cors_plug', '<ROOT>', ':max_age']"
path=":cors_plug.:max_age"
/>
</li>
<li>
<BooleanSetting
:description-path-override="[':cors_plug', '<ROOT>', ':credentials']"
path=":cors_plug.:credentials"
/>
</li>
<li>
<ListSetting
path=":cors_plug.:methods"
:description-path-override="[':cors_plug', '<ROOT>', ':methods']"
/>
</li>
<li>
<ListSetting
:description-path-override="[':cors_plug', '<ROOT>', ':expose']"
path=":cors_plug.:expose"
/>
</li>
<li>
<ListSetting
:description-path-override="[':cors_plug', '<ROOT>', ':headers']"
path=":cors_plug.:headers"
/>
</li>
<h4>{{ $t('admin_dash.http.security') }}</h4>
<li>
<BooleanSetting path=":pleroma.:http_security.:enabled" />
</li>
<li>
<BooleanSetting path=":pleroma.:http_security.:sts" />
</li>
<li>
<IntegerSetting path=":pleroma.:http_security.:sts_max_age" />
</li>
<li>
<IntegerSetting path=":pleroma.:http_security.:ct_max_age" />
</li>
<li>
<StringSetting path=":pleroma.:http_security.:referrer_policy" />
</li>
<li>
<BooleanSetting path=":pleroma.:http_security.:allow_unsafe_eval" />
</li>
<li>
<StringSetting path=":pleroma.:http_security.:report_url" />
</li>
<h4>{{ $t('admin_dash.http.web_cache_ttl') }}</h4>
<p>{{ $t('admin_dash.http.web_cache_ttl_description') }}</p>
<li>
<StringSetting path=":pleroma.:web_cache_ttl.:activity_pub" />
</li>
<li>
<StringSetting path=":pleroma.:web_cache_ttl.:activity_pub_question" />
</li>
</ul>
<h3>{{ $t('admin_dash.http.web_push') }}</h3>
<p>{{ $t('admin_dash.http.web_push_description') }}</p>
<ul class="setting-list">
<li>
<StringSetting
path=":web_push_encryption.:vapid_details.:subject"
/>
</li>
<li>
<StringSetting
path=":web_push_encryption.:vapid_details.:public_key"
/>
</li>
<li>
<StringSetting
path=":web_push_encryption.:vapid_details.:private_key"
/>
</li>
</ul>
<!-- CONFIRM admin_token should go there but something is wrong with both data and description. -->
<!-- given the nature of the setting it's probably better to not expose it and deprecate it on backend side -->
<!-- CONFIRM :pleroma.:streamer should also PROBABLY? go here but it's completely MIA from backend besides references to config description -->
</div>
</div>
</template>
<!--<style lang="scss" src="./http_tab.scss"></style>-->
<script src="./http_tab.js"></script>

View file

@ -2,18 +2,15 @@ import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import ColorSetting from '../helpers/color_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import PWAManifestIconsSetting from '../helpers/pwa_manifest_icons_setting.vue'
import MapSetting from '../helpers/map_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faGlobe
} from '@fortawesome/free-solid-svg-icons'
library.add(
faGlobe
)
import { get } from 'lodash'
const InstanceTab = {
provide () {
@ -27,11 +24,29 @@ const InstanceTab = {
ChoiceSetting,
IntegerSetting,
StringSetting,
ColorSetting,
AttachmentSetting,
ListSetting,
PWAManifestIconsSetting,
MapSetting,
GroupSetting
},
computed: {
...SharedComputedObject()
...SharedComputedObject(),
providersOptions () {
const desc = get(this.$store.state.adminSettings.descriptions, [':pleroma', 'Pleroma.Web.Metadata', ':providers'])
return new Set(desc.suggestions.map(option => ({
label: option.replace('Pleroma.Web.Metadata.Providers.', ''),
value: option
})))
},
limitLocalContentOptions () {
const desc = get(this.$store.state.adminSettings.descriptions, [':pleroma', ':instance', ':limit_to_local_content'])
return new Set(desc.suggestions.map(option => ({
label: option !== 'false' ? this.$t('admin_dash.instance.' + option) : this.$t('general.no'),
value: option
})))
}
}
}

View file

@ -1,17 +1,13 @@
<template>
<div :label="$t('admin_dash.tabs.instance')">
<div class="setting-item">
<h2>{{ $t('admin_dash.instance.instance') }}</h2>
<h3>{{ $t('admin_dash.instance.instance') }}</h3>
<ul class="setting-list">
<li>
<StringSetting path=":pleroma.:instance.:name" />
</li>
<!-- See https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3963 -->
<li v-if="adminDraft[':pleroma'][':instance'][':favicon'] !== undefined">
<AttachmentSetting
compact
path=":pleroma.:instance.:favicon"
/>
<li>
<StringSetting path=":pleroma.:instance.:contact_username" />
</li>
<li>
<StringSetting path=":pleroma.:instance.:email" />
@ -22,87 +18,63 @@
<li>
<StringSetting path=":pleroma.:instance.:short_description" />
</li>
<li>
<ListSetting
force-new
ignore-suggestions
path=":pleroma.:instance.:languages"
/>
</li>
<li>
<StringSetting path=":pleroma.:instance.:status_page" />
</li>
</ul>
<h3>{{ $t('admin_dash.instance.branding') }}</h3>
<ul class="setting-list">
<!-- See https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3963 -->
<li v-if="adminDraft[':pleroma'][':instance'][':favicon'] !== undefined">
<AttachmentSetting
compact
path=":pleroma.:instance.:favicon"
/>
</li>
<li>
<AttachmentSetting
compact
path=":pleroma.:instance.:instance_thumbnail"
/>
</li>
<li>
<PWAManifestIconsSetting path=":pleroma.:manifest.:icons" />
</li>
<li>
<ColorSetting path=":pleroma.:manifest.:theme_color" />
</li>
<li>
<ColorSetting path=":pleroma.:manifest.:background_color" />
</li>
<li>
<AttachmentSetting path=":pleroma.:instance.:background_image" />
</li>
</ul>
</div>
<div class="setting-item">
<h2>{{ $t('admin_dash.instance.registrations') }}</h2>
<h3>{{ $t('admin_dash.instance.rich_metadata') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:instance.:registrations_open" />
<ul class="setting-list suboptions">
<li>
<BooleanSetting
path=":pleroma.:instance.:invites_enabled"
parent-path=":pleroma.:instance.:registrations_open"
parent-invert
/>
</li>
</ul>
<ListSetting
override-available-options
:options="providersOptions"
:path="[':pleroma','Pleroma.Web.Metadata', ':providers']"
/>
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:birthday_required" />
<ul class="setting-list suboptions">
<li>
<IntegerSetting
path=":pleroma.:instance.:birthday_min_age"
parent-path=":pleroma.:instance.:birthday_required"
/>
</li>
</ul>
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:account_activation_required" />
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:account_approval_required" />
</li>
<li>
<h3>{{ $t('admin_dash.instance.captcha_header') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting :path="[':pleroma', 'Pleroma.Captcha', ':enabled']" />
<ul class="setting-list suboptions">
<li>
<ChoiceSetting
:path="[':pleroma', 'Pleroma.Captcha', ':method']"
:parent-path="[':pleroma', 'Pleroma.Captcha', ':enabled']"
:option-label-map="{
'Pleroma.Captcha.Native': $t('admin_dash.captcha.native'),
'Pleroma.Captcha.Kocaptcha': $t('admin_dash.captcha.kocaptcha')
}"
/>
<IntegerSetting
:path="[':pleroma', 'Pleroma.Captcha', ':seconds_valid']"
:parent-path="[':pleroma', 'Pleroma.Captcha', ':enabled']"
/>
</li>
<li
v-if="adminDraft[':pleroma']['Pleroma.Captcha'][':enabled'] && adminDraft[':pleroma']['Pleroma.Captcha'][':method'] === 'Pleroma.Captcha.Kocaptcha'"
>
<h4>{{ $t('admin_dash.instance.kocaptcha') }}</h4>
<ul class="setting-list">
<li>
<StringSetting :path="[':pleroma', 'Pleroma.Captcha.Kocaptcha', ':endpoint']" />
</li>
</ul>
</li>
</ul>
</li>
</ul>
<BooleanSetting
:path="[':pleroma','Pleroma.Web.Metadata', ':unfurl_nsfw']"
/>
</li>
</ul>
</div>
<div class="setting-item">
<h2>{{ $t('admin_dash.instance.access') }}</h2>
<h3>{{ $t('admin_dash.instance.access') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting
@ -115,6 +87,8 @@
<ChoiceSetting
override-backend-description
override-backend-description-label
override-available-options
:options="limitLocalContentOptions"
path=":pleroma.:instance.:limit_to_local_content"
/>
</li>

View file

@ -0,0 +1,34 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import TupleSetting from '../helpers/tuple_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
const JobQueuesTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting,
TupleSetting,
AttachmentSetting,
GroupSetting,
ListSetting
},
computed: {
...SharedComputedObject()
}
}
export default JobQueuesTab

View file

@ -0,0 +1,199 @@
<template>
<div :label="$t('admin_dash.tabs.job_queues')">
<div class="setting-item">
<h3>{{ $t('admin_dash.job_queues.Oban') }}</h3>
<ul class="setting-list">
<li>
<ChoiceSetting path=":pleroma.Oban.:log" />
<h4>{{ $t('admin_dash.job_queues.queues') }}</h4>
<ul class="setting-list">
<li>
<IntegerSetting path=":pleroma.Oban.:queues.:federator_incoming" />
</li>
<li>
<IntegerSetting path=":pleroma.Oban.:queues.:federator_outgoing" />
</li>
<li>
<IntegerSetting path=":pleroma.Oban.:queues.:attachments_cleanup" />
</li>
<li>
<IntegerSetting path=":pleroma.Oban.:queues.:mailer" />
</li>
<li>
<IntegerSetting path=":pleroma.Oban.:queues.:activity_expiration" />
</li>
<li>
<IntegerSetting path=":pleroma.Oban.:queues.:scheduled_activities" />
</li>
<li>
<IntegerSetting path=":pleroma.Oban.:queues.:transmogrifier" />
</li>
<li>
<IntegerSetting path=":pleroma.Oban.:queues.:web_push" />
</li>
<!-- CONFIRM what is this queue -->
<li>
<IntegerSetting path=":pleroma.Oban.:queues.:slow" />
</li>
<!-- CONFIRM what is this queue -->
<li>
<IntegerSetting path=":pleroma.Oban.:queues.:background" />
</li>
</ul>
<GroupSetting path=":pleroma.Oban.:queues" />
</li>
</ul>
<h3>{{ $t('admin_dash.job_queues.Gun.title') }}</h3>
<ul class="setting-list">
<li>
<h4>{{ $t('admin_dash.job_queues.Gun.connections_pools') }}</h4>
<ul class="setting-list">
<li>
<IntegerSetting path=":pleroma.:connections_pool.:connect_timeout" />
</li>
<li>
<IntegerSetting path=":pleroma.:connections_pool.:connection_acquisition_retries" />
</li>
<li>
<IntegerSetting path=":pleroma.:connections_pool.:connection_acquisition_wait" />
</li>
<li>
<!-- CONFIRM what is this -->
<IntegerSetting path=":pleroma.:connections_pool.:retry" />
</li>
<li>
<IntegerSetting path=":pleroma.:connections_pool.:max_connections" />
</li>
<li>
<!-- CONFIRM what is this -->
<IntegerSetting path=":pleroma.:connections_pool.:max_idle_time" />
</li>
<li>
<IntegerSetting path=":pleroma.:connections_pool.:reclaim_multiplier" />
</li>
</ul>
</li>
<li>
<h4>{{ $t('admin_dash.job_queues.Gun.pools.title') }}</h4>
<ul class="setting-list">
<li>
<h5>{{ $t('admin_dash.job_queues.Gun.pools.default') }}</h5>
<ul class="setting-list suboptions">
<li>
<IntegerSetting path=":pleroma.:pools.:default.:size" />
</li>
<li>
<IntegerSetting path=":pleroma.:pools.:default.:max_waiting" />
</li>
<li>
<IntegerSetting path=":pleroma.:pools.:default.:recv_timeout" />
</li>
</ul>
<GroupSetting path=":pleroma.:pools.:default" />
</li>
<li>
<h5>{{ $t('admin_dash.job_queues.Gun.pools.federation') }}</h5>
<ul class="setting-list suboptions">
<li>
<IntegerSetting path=":pleroma.:pools.:federation.:size" />
</li>
<li>
<IntegerSetting path=":pleroma.:pools.:federation.:max_waiting" />
</li>
<li>
<IntegerSetting path=":pleroma.:pools.:federation.:recv_timeout" />
</li>
</ul>
<GroupSetting path=":pleroma.:pools.:federation" />
</li>
<li>
<!-- CONFIRM what is this? -->
<h5>{{ $t('admin_dash.job_queues.Gun.pools.rich_media') }}</h5>
<ul class="setting-list suboptions">
<li>
<IntegerSetting path=":pleroma.:pools.:rich_media.:size" />
</li>
<li>
<IntegerSetting path=":pleroma.:pools.:rich_media.:max_waiting" />
</li>
<li>
<IntegerSetting path=":pleroma.:pools.:rich_media.:recv_timeout" />
</li>
</ul>
<GroupSetting path=":pleroma.:pools.:rich_media" />
</li>
<li>
<h5>{{ $t('admin_dash.job_queues.Gun.pools.media') }}</h5>
<ul class="setting-list suboptions">
<li>
<IntegerSetting path=":pleroma.:pools.:media.:size" />
</li>
<li>
<IntegerSetting path=":pleroma.:pools.:media.:max_waiting" />
</li>
<li>
<IntegerSetting path=":pleroma.:pools.:media.:recv_timeout" />
</li>
</ul>
<GroupSetting path=":pleroma.:pools.:media" />
</li>
</ul>
</li>
</ul>
<h3>{{ $t('admin_dash.job_queues.Hackney.title') }}</h3>
<ul class="setting-list">
<li>
<h4>{{ $t('admin_dash.job_queues.Hackney.federation') }}</h4>
<ul class="setting-list">
<li>
<IntegerSetting path=":pleroma.:hackney_pools.:federation.:max_connections" />
</li>
<li>
<IntegerSetting path=":pleroma.:hackney_pools.:federation.:timeout" />
</li>
</ul>
<GroupSetting path=":pleroma.:hackney_pools.:federation" />
</li>
<li>
<h4>{{ $t('admin_dash.job_queues.Hackney.media') }}</h4>
<ul class="setting-list">
<li>
<IntegerSetting path=":pleroma.:hackney_pools.:media.:max_connections" />
</li>
<li>
<IntegerSetting path=":pleroma.:hackney_pools.:media.:timeout" />
</li>
</ul>
<GroupSetting path=":pleroma.:hackney_pools.:media" />
</li>
<li>
<!-- CONFIRM what is this -->
<h4>{{ $t('admin_dash.job_queues.Hackney.rich_media') }}</h4>
<ul class="setting-list">
<li>
<IntegerSetting path=":pleroma.:hackney_pools.:rich_media.:max_connections" />
</li>
<li>
<IntegerSetting path=":pleroma.:hackney_pools.:rich_media.:timeout" />
</li>
</ul>
<GroupSetting path=":pleroma.:hackney_pools.:rich_media" />
</li>
<li>
<h4>{{ $t('admin_dash.job_queues.Hackney.upload') }}</h4>
<ul class="setting-list">
<li>
<IntegerSetting path=":pleroma.:hackney_pools.:upload.:max_connections" />
</li>
<li>
<IntegerSetting path=":pleroma.:hackney_pools.:upload.:timeout" />
</li>
</ul>
<GroupSetting path=":pleroma.:hackney_pools.:upload" />
</li>
</ul>
</div>
</div>
</template>
<script src="./job_queues_tab.js"></script>

View file

@ -4,14 +4,6 @@ import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faGlobe
} from '@fortawesome/free-solid-svg-icons'
library.add(
faGlobe
)
const LimitsTab = {
components: {

View file

@ -1,10 +1,10 @@
<template>
<div :label="$t('admin_dash.tabs.limits')">
<div class="setting-item">
<h2>{{ $t('admin_dash.limits.arbitrary_limits') }}</h2>
<h3>{{ $t('admin_dash.limits.arbitrary_limits') }}</h3>
<ul class="setting-list">
<li>
<h3>{{ $t('admin_dash.limits.posts') }}</h3>
<h4>{{ $t('admin_dash.limits.posts') }}</h4>
<ul class="setting-list">
<li>
<IntegerSetting
@ -24,7 +24,7 @@
</ul>
</li>
<li>
<h3>{{ $t('admin_dash.limits.uploads') }}</h3>
<h4>{{ $t('admin_dash.limits.uploads') }}</h4>
<ul class="setting-list">
<li>
<IntegerSetting
@ -50,7 +50,7 @@
</ul>
</li>
<li>
<h3>{{ $t('admin_dash.limits.users') }}</h3>
<h4>{{ $t('admin_dash.limits.users') }}</h4>
<ul class="setting-list">
<li>
<IntegerSetting
@ -74,7 +74,7 @@
/>
</li>
<li>
<h4>{{ $t('admin_dash.limits.profile_fields') }}</h4>
<h5>{{ $t('admin_dash.limits.profile_fields') }}</h5>
<ul class="setting-list">
<li>
<IntegerSetting
@ -108,7 +108,7 @@
</ul>
</li>
<li>
<h4>{{ $t('admin_dash.limits.user_uploads') }}</h4>
<h5>{{ $t('admin_dash.limits.user_uploads') }}</h5>
<ul class="setting-list">
<li>
<IntegerSetting
@ -128,6 +128,25 @@
</li>
</ul>
</li>
<li>
<h4>{{ $t('admin_dash.limits.other') }}</h4>
<ul class="setting-list">
<li>
<IntegerSetting
source="admin"
path=":pleroma.:instance.:max_report_comment_size"
draft-mode
/>
</li>
<li>
<IntegerSetting
source="admin"
path=":pleroma.:instance.:max_endorsed_users"
draft-mode
/>
</li>
</ul>
</li>
</ul>
</div>
</div>

View file

@ -0,0 +1,109 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import Checkbox from 'src/components/checkbox/checkbox.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
import { get } from 'lodash'
const LinksTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting,
AttachmentSetting,
GroupSetting,
ListSetting,
Checkbox
},
computed: {
classIsPresent () {
return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Formatter'][':class'] !== false
},
relIsPresent () {
return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Formatter'][':rel'] !== false
},
truncateIsPresent () {
return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Formatter'][':truncate'] !== false
},
truncateDescription () {
return get(this.$store.state.adminSettings.descriptions, [':pleroma', 'Pleroma.Formatter', ':truncate'])
},
ttlSettersOptions () {
const desc = get(this.$store.state.adminSettings.descriptions, ':pleroma.:rich_media.:ttl_setters')
return new Set(desc.suggestions.map(option => ({
label: option.replace('Pleroma.Web.RichMedia.Parser.TTL.', ''),
value: option
})))
},
parsersOptions () {
const desc = get(this.$store.state.adminSettings.descriptions, ':pleroma.:rich_media.:parsers')
return new Set(desc.suggestions.map(option => ({
label: option.replace('Pleroma.Web.RichMedia.Parsers.', ''),
value: option
})))
},
validateTLDOptions () {
return [{
label: this.$t('general.yes'),
value: true
}, {
label: this.$t('general.no'),
value: false
}, {
label: this.$t('admin_dash.links.no_scheme'),
value: ':no_scheme'
}]
},
mediaProxyEnabled () {
return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][':enabled']
},
mediaInvalidationProvider () {
return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][':invalidation'][':provider']
},
...SharedComputedObject()
},
methods: {
checkRel (e) {
this.$store.commit(
'updateAdminDraft',
{
path: [':pleroma','Pleroma.Formatter',':rel'],
value: e ? '' : false
}
)
},
checkClass (e) {
this.$store.commit(
'updateAdminDraft',
{
path: [':pleroma','Pleroma.Formatter',':class'],
value: e ? '' : false
}
)
},
checkTruncate (e) {
this.$store.commit(
'updateAdminDraft',
{
path: [':pleroma','Pleroma.Formatter',':truncate'],
value: e ? 20 : false
}
)
}
}
}
export default LinksTab

View file

@ -0,0 +1,26 @@
.LinksTab {
// awkward "disable = false" backend options
.weird-options {
.checkbox {
margin: 0;
}
}
.setting-list.suboptions.weird-suboptions {
display: flex;
flex-direction: column;
gap: 0.5em;
margin: 0;
li {
margin: 0;
display: flex;
}
.GroupSetting {
display: inline-block;
margin: 0;
padding: 0;
}
}
}

View file

@ -0,0 +1,146 @@
<template>
<div
class="LinksTab"
:label="$t('admin_dash.tabs.media_proxy')"
>
<div class="setting-item">
<h3>{{ $t('admin_dash.links.link_previews') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:rich_media.:enabled" />
</li>
<li>
<ListSetting
override-available-options
:options="parsersOptions"
path=":pleroma.:rich_media.:parsers"
/>
</li>
<li>
<IntegerSetting
path=":pleroma.:rich_media.:timeout"
/>
</li>
<li>
<ListSetting
override-available-options
:options="ttlSettersOptions"
path=":pleroma.:rich_media.:ttl_setters"
/>
</li>
<li>
<ListSetting
path=":pleroma.:rich_media.:ignore_tld"
ignore-suggestions
/>
</li>
<li>
<ListSetting path=":pleroma.:rich_media.:ignore_hosts" />
</li>
</ul>
<h3>{{ $t('admin_dash.links.link_formatter') }}</h3>
<ul class="setting-list weird-options">
<li>
<Checkbox
:model-value="classIsPresent"
@update:model-value="checkClass"
>
<i18n-t
keypath="admin_dash.temp_overrides.:pleroma.Pleroma_DOT_Formatter.:attribute_toggle.label"
tag="span"
scope="global"
>
<template #attr>
<code>class</code>
</template>
</i18n-t>
</Checkbox>
<div class="setting-list suboptions weird-suboptions">
<StringSetting
v-if="classIsPresent"
:path="[':pleroma', 'Pleroma.Formatter', ':class']"
hide-label
hide-draft-buttons
/>
<GroupSetting :path="[':pleroma', 'Pleroma.Formatter', ':class']" />
</div>
</li>
<li>
<Checkbox
:model-value="relIsPresent"
@update:model-value="checkRel"
>
<i18n-t
keypath="admin_dash.temp_overrides.:pleroma.Pleroma_DOT_Formatter.:attribute_toggle.label"
tag="span"
scope="global"
>
<template #attr>
<code>rel</code>
</template>
</i18n-t>
</Checkbox>
<div class="setting-list suboptions weird-suboptions">
<StringSetting
v-if="relIsPresent"
:path="[':pleroma', 'Pleroma.Formatter', ':rel']"
hide-label
hide-draft-buttons
/>
<GroupSetting
:path="[':pleroma', 'Pleroma.Formatter', ':rel']"
/>
</div>
</li>
<li>
<BooleanSetting :path="[':pleroma', 'Pleroma.Formatter', ':new_window']" />
</li>
<li>
<BooleanSetting :path="[':pleroma', 'Pleroma.Formatter', ':strip_prefix']" />
</li>
<li>
<BooleanSetting :path="[':pleroma', 'Pleroma.Formatter', ':extra']" />
</li>
<li>
<ChoiceSetting
:path="[':pleroma', 'Pleroma.Formatter', ':validate_tld']"
:options="validateTLDOptions"
override-options
/>
</li>
<li>
<Checkbox
:model-value="truncateIsPresent"
@update:model-value="checkTruncate"
>
{{ truncateDescription.label }}
</Checkbox>
<div class="setting-list suboptions weird-suboptions">
<li>
<IntegerSetting
v-if="truncateIsPresent"
:path="[':pleroma', 'Pleroma.Formatter', ':truncate']"
hide-label
hide-draft-buttons
/>
</li>
<li>
<GroupSetting :path="[':pleroma', 'Pleroma.Formatter', ':truncate']" />
</li>
</div>
</li>
<li>
<!-- CONFIRM backend what's difference between here and :extra -->
<ListSetting
ignore-suggestions
path=":pleroma.:uri_schemes.:valid_schemes"
/>
</li>
</ul>
</div>
</div>
</template>
<script src="./links_tab.js"></script>
<style lang="scss" src="./links_tab.scss"></style>

View file

@ -0,0 +1,63 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
const MailerTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting,
AttachmentSetting,
GroupSetting
},
computed: {
adaptersLabels () {
const prefix = 'Swoosh.Adapters.'
const descriptions = this.$store.state.adminSettings.descriptions
const options = descriptions[':pleroma']['Pleroma.Emails.Mailer'][':adapter'].suggestions
return Object.fromEntries(options.map(value => [
value, value.replace(prefix, '')
]))
},
startTLSLabels () {
return {
':always': this.$t('admin_dash.generic_enforcement.always'),
':if_available': this.$t('admin_dash.generic_enforcement.if_available'),
':never': this.$t('admin_dash.generic_enforcement.never')
}
// return Object.fromEntries(options.map(value => [
// value, value.replace(prefix, '')
// ]))
},
adapter () {
return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Emails.Mailer'][':adapter']
},
mailerEnabled () {
return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Emails.Mailer'][':enabled']
},
...SharedComputedObject()
},
methods: {
adapterHasKey (key) {
const descriptions = this.$store.state.adminSettings.descriptions
const mailerStuff = descriptions[':pleroma']['Pleroma.Emails.Mailer']
const adapterStuff = mailerStuff[':subgroup,' + this.adapter]
return Object.prototype.hasOwnProperty.call(adapterStuff, key)
}
}
}
export default MailerTab

View file

@ -0,0 +1,150 @@
<template>
<div :label="$t('admin_dash.tabs.mailer')">
<div class="setting-item">
<h3>{{ $t('admin_dash.mailer.adapter') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting :path="[':pleroma','Pleroma.Emails.Mailer',':enabled']" />
</li>
<template v-if="mailerEnabled">
<li>
<ChoiceSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':adapter']"
:option-label-map="adaptersLabels"
/>
<h4>{{ $t('admin_dash.mailer.auth') }}</h4>
<ul class="setting-list suboptions">
<li v-if="adapterHasKey(':api_key')">
<!-- authentication info -->
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':api_key']"
:password="true"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':access_key')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':access_key']"
:password="true"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':access_token')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':access_token']"
:password="true"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':username')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':username']"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':password')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':password']"
:password="true"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':secret')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':secret']"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':auth')">
<ChoiceSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':auth']"
:password="true"
:subgroup="adapter"
/>
</li>
<!-- server info -->
<li v-if="adapterHasKey(':relay')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':relay']"
:password="true"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':ssl')">
<BooleanSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':ssl']"
:password="true"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':tls')">
<ChoiceSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':tls']"
:option-label-map="startTLSLabels"
:password="true"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':port')">
<IntegerSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':port']"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':server_id')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':server_id']"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':region')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':region']"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':domain')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':domain']"
:subgroup="adapter"
/>
</li>
<!-- sendmail exclusive -->
<li v-if="adapterHasKey(':cmd_path')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':cmd_path']"
:password="true"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':cmd_args')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':cmd_args']"
:password="true"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':qmail')">
<BooleanSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':qmail']"
:password="true"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':retries')">
<IntegerSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':retries']"
:subgroup="adapter"
/>
</li>
</ul>
</li>
</template>
</ul>
</div>
</div>
</template>
<script src="./mailer_tab.js"></script>

View file

@ -0,0 +1,38 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
const MediaProxyTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting,
AttachmentSetting,
GroupSetting,
ListSetting
},
computed: {
mediaProxyEnabled () {
return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][':enabled']
},
mediaInvalidationProvider () {
return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][':invalidation'][':provider']
},
...SharedComputedObject()
}
}
export default MediaProxyTab

View file

@ -0,0 +1,135 @@
<template>
<div :label="$t('admin_dash.tabs.media_proxy')">
<div class="setting-item">
<h3>{{ $t('admin_dash.media_proxy.basic') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:media_proxy.:enabled" />
<ul
v-if="mediaProxyEnabled"
class="setting-list suboptions"
>
<li>
<StringSetting path=":pleroma.:media_proxy.:base_url" />
</li>
<li>
<BooleanSetting path=":pleroma.:media_proxy.:proxy_opts.:redirect_on_failure" />
</li>
<li>
<ListSetting
ignore-suggestions
path=":pleroma.:media_proxy.:whitelist"
/>
</li>
</ul>
</li>
</ul>
<template v-if="mediaProxyEnabled">
<h3>{{ $t('admin_dash.media_proxy.invalidation') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:media_proxy.:invalidation.:enabled" />
<ul class="setting-list suboptions">
<li>
<ChoiceSetting
path=":pleroma.:media_proxy.:invalidation.:provider"
parent-path=":pleroma.:media_proxy.:invalidation.:enabled"
/>
</li>
<h4>{{ $t('admin_dash.media_proxy.invalidation_settings') }}</h4>
<ul class="setting-list suboptions">
<template v-if="mediaInvalidationProvider === 'Pleroma.Web.MediaProxy.Invalidation.Http'">
<li>
<StringSetting
:path="[':pleroma', 'Pleroma.Web.MediaProxy.Invalidation.Http', ':method']"
parent-path=":pleroma.:media_proxy.:invalidation.:enabled"
/>
</li>
<li>
<ListSetting
ignore-suggestions
:path="[':pleroma', 'Pleroma.Web.MediaProxy.Invalidation.Http', ':headers']"
parent-path=":pleroma.:media_proxy.:invalidation.:enabled"
/>
</li>
<li>
<ListSetting
:path="[':pleroma', 'Pleroma.Web.MediaProxy.Invalidation.Http', ':options']"
parent-path=":pleroma.:media_proxy.:invalidation.:enabled"
/>
</li>
</template>
<template v-if="mediaInvalidationProvider === 'Pleroma.Web.MediaProxy.Invalidation.Script'">
<!-- TODO: you know the drill by now - list component -->
<li>
<StringSetting
:path="[':pleroma', 'Pleroma.Web.MediaProxy.Invalidation.Script', ':script_path']"
parent-path=":pleroma.:media_proxy.:invalidation.:enabled"
/>
</li>
<li>
<StringSetting
:path="[':pleroma', 'Pleroma.Web.MediaProxy.Invalidation.Script', ':url_format']"
parent-path=":pleroma.:media_proxy.:invalidation.:enabled"
/>
</li>
</template>
</ul>
</ul>
</li>
</ul>
<h3>{{ $t('admin_dash.media_proxy.limits') }}</h3>
<ul class="setting-list">
<li>
<IntegerSetting
path=":pleroma.:media_proxy.:proxy_opts.:max_body_length"
/>
</li>
<li>
<IntegerSetting
path=":pleroma.:media_proxy.:proxy_opts.:max_read_duration"
/>
</li>
<li>
<GroupSetting path=":pleroma.:media_proxy.:proxy_opts" />
</li>
</ul>
<!-- TODO: add whitelist when we have list component (hehe) -->
<h3>{{ $t('admin_dash.media_proxy.thumbnails') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:media_preview_proxy.:enabled" />
<ul class="setting-list suboptions">
<li>
<IntegerSetting
parent-path=":pleroma.:media_preview_proxy.:enabled"
path=":pleroma.:media_preview_proxy.:image_quality"
/>
</li>
<li>
<IntegerSetting
parent-path=":pleroma.:media_preview_proxy.:enabled"
path=":pleroma.:media_preview_proxy.:min_content_length"
/>
</li>
<li>
<IntegerSetting
parent-path=":pleroma.:media_preview_proxy.:enabled"
path=":pleroma.:media_preview_proxy.:thumbnail_max_width"
/>
</li>
<li>
<IntegerSetting
parent-path=":pleroma.:media_preview_proxy.:enabled"
path=":pleroma.:media_preview_proxy.:thumbnail_max_height"
/>
</li>
</ul>
</li>
</ul>
</template>
</div>
</div>
</template>
<script src="./media_proxy_tab.js"></script>

View file

@ -0,0 +1,42 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faGlobe
} from '@fortawesome/free-solid-svg-icons'
library.add(
faGlobe
)
const MonitoringTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting,
AttachmentSetting,
GroupSetting,
ListSetting
},
computed: {
...SharedComputedObject()
},
methods: {
}
}
export default MonitoringTab

View file

@ -0,0 +1,47 @@
<template>
<div :label="$t('admin_dash.tabs.monitoring')">
<div class="setting-item">
<h3>{{ $t('admin_dash.monitoring.builtins') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting :path="[':pleroma','Pleroma.Emails.NewUsersDigestEmail',':enabled']" />
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:healthcheck" />
</li>
</ul>
<h3>{{ $t('admin_dash.monitoring.prometheus') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting :path="[':prometheus','Pleroma.Web.Endpoint.MetricsExporter',':enabled']" />
</li>
<li>
<BooleanSetting
:parent-path="[':prometheus','Pleroma.Web.Endpoint.MetricsExporter',':enabled']"
:path="[':prometheus','Pleroma.Web.Endpoint.MetricsExporter',':auth']"
/>
</li>
<li>
<StringSetting
:parent-path="[':prometheus','Pleroma.Web.Endpoint.MetricsExporter',':enabled']"
:path="[':prometheus','Pleroma.Web.Endpoint.MetricsExporter',':path']"
/>
</li>
<li>
<ChoiceSetting
:parent-path="[':prometheus','Pleroma.Web.Endpoint.MetricsExporter',':enabled']"
:path="[':prometheus','Pleroma.Web.Endpoint.MetricsExporter',':format']"
/>
</li>
<li>
<ListSetting
:parent-path="[':prometheus','Pleroma.Web.Endpoint.MetricsExporter',':enabled']"
:path="[':prometheus','Pleroma.Web.Endpoint.MetricsExporter',':ip_whitelist']"
/>
</li>
</ul>
</div>
</div>
</template>
<script src="./monitoring_tab.js"></script>

View file

@ -0,0 +1,38 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import ColorSetting from '../helpers/color_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import PWAManifestIconsSetting from '../helpers/pwa_manifest_icons_setting.vue'
import MapSetting from '../helpers/map_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
const OtherTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting,
ColorSetting,
AttachmentSetting,
ListSetting,
PWAManifestIconsSetting,
MapSetting,
GroupSetting
},
computed: {
...SharedComputedObject()
}
}
export default OtherTab

View file

@ -0,0 +1,134 @@
<template>
<div :label="$t('admin_dash.tabs.other')">
<div class="setting-item">
<h3>{{ $t('admin_dash.other.uncategorized') }}</h3>
<ul class="setting-list">
<li>
<StringSetting path=":pleroma.:instance.:static_dir" />
</li>
<li>
<StringSetting path=":pleroma.:modules.:runtime_dir" />
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:profile_directory" />
</li>
<li>
<BooleanSetting :path="[':pleroma','Pleroma.Web.ApiSpec.CastAndValidate',':strict']" />
</li>
</ul>
<h3>{{ $t('admin_dash.other.reports') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:instance.:report_strip_status" />
</li>
</ul>
<h3>{{ $t('admin_dash.other.user_backup') }}</h3>
<ul class="setting-list">
<li>
<IntegerSetting :path="[':pleroma','Pleroma.User.Backup',':purge_after_days']" />
</li>
<li>
<IntegerSetting :path="[':pleroma','Pleroma.User.Backup',':limit_days']" />
</li>
<!-- CONFIRM what is this?
<li>
<StringSetting :path="[':pleroma','Pleroma.User.Backup',':dir']" />
</li>
-->
<li>
<IntegerSetting :path="[':pleroma','Pleroma.User.Backup',':process_chunk_size']" />
</li>
<li>
<IntegerSetting :path="[':pleroma','Pleroma.User.Backup',':timeout']" />
</li>
</ul>
<h3>{{ $t('admin_dash.other.feed') }}</h3>
<ul class="setting-list">
<h4>{{ $t('admin_dash.other.feed_title') }}</h4>
<li>
<IntegerSetting path=":pleroma.:feed.:post_title.:max_length" />
</li>
<li>
<StringSetting path=":pleroma.:feed.:post_title.:omission" />
</li>
<li>
<GroupSetting path=":pleroma.:feed.:post_title" />
</li>
</ul>
<h3>{{ $t('admin_dash.other.remote_ip') }}</h3>
<p>{{ $t('admin_dash.other.remote_ip_description') }}</p>
<ul class="setting-list">
<li>
<BooleanSetting :path="[':pleroma','Pleroma.Web.Plugs.RemoteIp',':enabled']" />
</li>
<li>
<ListSetting :path="[':pleroma','Pleroma.Web.Plugs.RemoteIp',':headers']" />
</li>
<li>
<ListSetting :path="[':pleroma','Pleroma.Web.Plugs.RemoteIp',':proxies']" />
</li>
<li>
<ListSetting :path="[':pleroma','Pleroma.Web.Plugs.RemoteIp',':reserved']" />
</li>
</ul>
<h3>{{ $t('admin_dash.other.mime') }}</h3>
<ul class="setting-list">
<li>
<ListSetting
override-backend-description-label="application/activity+json"
hide-description
:path="[':mime',':types', 'application/activity+json']"
/>
</li>
<li>
<ListSetting
override-backend-description-label="application/jrd+json"
hide-description
:path="[':mime',':types', 'application/jrd+json']"
/>
</li>
<li>
<ListSetting
override-backend-description-label="application/ld+json"
hide-description
:path="[':mime',':types', 'application/ld+json']"
/>
</li>
<li>
<ListSetting
override-backend-description-label="application/xml"
hide-description
:path="[':mime',':types', 'application/xml']"
/>
</li>
<li>
<ListSetting
override-backend-description-label="application/xrd+xml"
hide-description
:path="[':mime',':types', 'application/xrd+xml']"
/>
</li>
</ul>
<h3>{{ $t('admin_dash.other.streamer') }}</h3>
<ul class="setting-list">
<li>
<IntegerSetting path=":pleroma.:streamer.:workers" />
</li>
<li>
<IntegerSetting path=":pleroma.:streamer.:overflow_workers" />
</li>
</ul>
<h3>{{ $t('admin_dash.other.privileges') }}</h3>
<ul class="setting-list">
<li>
<ListSetting path=":pleroma.:instance.:admin_privileges" />
</li>
<li>
<ListSetting path=":pleroma.:instance.:moderator_privileges" />
</li>
</ul>
</div>
</div>
</template>
<script src="./other_tab.js"></script>

View file

@ -0,0 +1,38 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import ColorSetting from '../helpers/color_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import PWAManifestIconsSetting from '../helpers/pwa_manifest_icons_setting.vue'
import MapSetting from '../helpers/map_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
const PostsTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting,
ColorSetting,
AttachmentSetting,
ListSetting,
PWAManifestIconsSetting,
MapSetting,
GroupSetting
},
computed: {
...SharedComputedObject()
}
}
export default PostsTab

View file

@ -0,0 +1,43 @@
<template>
<div :label="$t('admin_dash.tabs.posts')">
<div class="setting-item">
<h3>{{ $t('admin_dash.posts.global') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:instances_favicons.:enabled" />
</li>
</ul>
<h3>{{ $t('admin_dash.posts.local') }}</h3>
<ul class="setting-list">
<li>
<ListSetting path=":pleroma.:instance.:allowed_post_formats" />
</li>
<li>
<h4>{{ $t('admin_dash.posts.scheduled_activites') }}</h4>
<ul class="setting-list">
<li>
<BooleanSetting :path="[':pleroma', 'Pleroma.ScheduledActivity', ':enabled']" />
</li>
<li>
<IntegerSetting :path="[':pleroma', 'Pleroma.ScheduledActivity', ':daily_user_limit']" />
</li>
<li>
<IntegerSetting :path="[':pleroma', 'Pleroma.ScheduledActivity', ':total_user_limit']" />
</li>
</ul>
</li>
</ul>
<h3>{{ $t('admin_dash.posts.remote') }}</h3>
<ul class="setting-list">
<li>
<IntegerSetting path=":pleroma.:instance.:remote_post_retention_days" />
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:skip_thread_containment" />
</li>
</ul>
</div>
</div>
</template>
<script src="./posts_tab.js"></script>

View file

@ -0,0 +1,34 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import TupleSetting from '../helpers/tuple_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
const RegistrationsTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting,
TupleSetting,
AttachmentSetting,
GroupSetting,
ListSetting
},
computed: {
...SharedComputedObject()
}
}
export default RegistrationsTab

View file

@ -0,0 +1,188 @@
<template>
<div :label="$t('admin_dash.tabs.instance')">
<div class="setting-item">
<h3>{{ $t('admin_dash.instance.registrations') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:instance.:registrations_open" />
<ul class="setting-list suboptions">
<li>
<BooleanSetting
path=":pleroma.:instance.:invites_enabled"
parent-path=":pleroma.:instance.:registrations_open"
parent-invert
/>
</li>
</ul>
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:birthday_required" />
<ul class="setting-list suboptions">
<li>
<IntegerSetting
path=":pleroma.:instance.:birthday_min_age"
parent-path=":pleroma.:instance.:birthday_required"
/>
</li>
</ul>
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:account_activation_required" />
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:account_approval_required" />
</li>
<li>
<h4>{{ $t('admin_dash.instance.captcha_header') }}</h4>
<ul class="setting-list">
<li>
<BooleanSetting :path="[':pleroma', 'Pleroma.Captcha', ':enabled']" />
<ul class="setting-list suboptions">
<li>
<ChoiceSetting
:path="[':pleroma', 'Pleroma.Captcha', ':method']"
:parent-path="[':pleroma', 'Pleroma.Captcha', ':enabled']"
:option-label-map="{
'Pleroma.Captcha.Native': $t('admin_dash.captcha.native'),
'Pleroma.Captcha.Kocaptcha': $t('admin_dash.captcha.kocaptcha')
}"
/>
<IntegerSetting
:path="[':pleroma', 'Pleroma.Captcha', ':seconds_valid']"
:parent-path="[':pleroma', 'Pleroma.Captcha', ':enabled']"
/>
</li>
<li
v-if="adminDraft[':pleroma']['Pleroma.Captcha'][':enabled'] && adminDraft[':pleroma']['Pleroma.Captcha'][':method'] === 'Pleroma.Captcha.Kocaptcha'"
>
<h5>{{ $t('admin_dash.instance.kocaptcha') }}</h5>
<ul class="setting-list">
<li>
<StringSetting :path="[':pleroma', 'Pleroma.Captcha.Kocaptcha', ':endpoint']" />
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3>{{ $t('admin_dash.registrations.autofollow') }}</h3>
<ul class="setting-list">
<li>
<ListSetting
path=":pleroma.:instance.:autofollowed_nicknames"
/>
</li>
<li>
<ListSetting
path=":pleroma.:instance.:autofollowing_nicknames"
/>
</li>
</ul>
<h3>{{ $t('admin_dash.registrations.welcome.title') }}</h3>
<ul class="setting-list">
<p>{{ $t('admin_dash.registrations.welcome.description') }}</p>
<li>
<h4>{{ $t('admin_dash.registrations.welcome.direct_message') }}</h4>
<ul class="setting-list">
<li>
<BooleanSetting
path=":pleroma.:welcome.:direct_message.:enabled"
/>
<ul class="setting-list suboptions">
<li>
<StringSetting
path=":pleroma.:welcome.:direct_message.:sender_nickname"
parent-path=":pleroma.:welcome.:direct_message.:enabled"
/>
</li>
<li>
<StringSetting
path=":pleroma.:welcome.:direct_message.:message"
parent-path=":pleroma.:welcome.:direct_message.:enabled"
/>
</li>
</ul>
<GroupSetting path=":pleroma.:welcome.:direct_message" />
</li>
</ul>
</li>
<li>
<h4>{{ $t('admin_dash.registrations.welcome.chat_message') }}</h4>
<ul class="setting-list">
<li>
<BooleanSetting
path=":pleroma.:welcome.:chat_message.:enabled"
/>
<ul class="setting-list suboptions">
<li>
<StringSetting
tuple
path=":pleroma.:welcome.:chat_message.:sender_nickname"
parent-path=":pleroma.:welcome.:chat_message.:enabled"
/>
</li>
<li>
<StringSetting
path=":pleroma.:welcome.:chat_message.:message"
parent-path=":pleroma.:welcome.:chat_message.:enabled"
/>
</li>
</ul>
<GroupSetting path=":pleroma.:welcome.:chat_message" />
</li>
</ul>
</li>
<li>
<h4>{{ $t('admin_dash.registrations.welcome.email_message') }}</h4>
<ul class="setting-list">
<li>
<BooleanSetting
path=":pleroma.:welcome.:email.:enabled"
/>
<ul class="setting-list suboptions">
<li>
<TupleSetting
path=":pleroma.:welcome.:email.:sender"
parent-path=":pleroma.:welcome.:email.:enabled"
/>
</li>
<li>
<StringSetting
path=":pleroma.:welcome.:email.:subject"
parent-path=":pleroma.:welcome.:email.:enabled"
/>
</li>
<li>
<StringSetting
path=":pleroma.:welcome.:email.:html"
parent-path=":pleroma.:welcome.:email.:enabled"
/>
</li>
</ul>
<GroupSetting path=":pleroma.:welcome.:email" />
</li>
</ul>
</li>
</ul>
<h3>{{ $t('admin_dash.registrations.restrictions') }}</h3>
<ul class="setting-list">
<li>
<ListSetting
ignore-suggestions
:path="[':pleroma', 'Pleroma.User', ':restricted_nicknames']"
/>
</li>
<li>
<ListSetting
ignore-suggestions
:path="[':pleroma', 'Pleroma.User', ':email_blacklist']"
/>
</li>
</ul>
</div>
</div>
</template>
<script src="./registrations_tab.js"></script>

View file

@ -0,0 +1,46 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
const UploadsTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
data () {
return {
uploaders: [{
key: 'Pleroma.Uploaders.Local',
value: 'Pleroma.Uploaders.Local',
label: this.$t('admin_dash.uploads.local_uploader')
}, {
key: 'Pleroma.Uploaders.IPFS',
value: 'Pleroma.Uploaders.IPFS',
label: 'IPFS'
}, {
key: 'Pleroma.Uploaders.S3',
value: 'Pleroma.Uploaders.S3',
label: 'S3'
}]
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting
},
computed: {
uploader () {
return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Upload'][':uploader']
},
...SharedComputedObject()
}
}
export default UploadsTab

View file

@ -0,0 +1,110 @@
<template>
<div :label="$t('admin_dash.tabs.uploads')">
<div class="setting-item">
<h3>{{ $t('admin_dash.uploads.upload') }}</h3>
<ul class="setting-list">
<li>
<ChoiceSetting
:path="[':pleroma','Pleroma.Upload',':uploader']"
:options="uploaders"
/>
<h4>{{ $t('admin_dash.uploads.uploader_settings') }}</h4>
<ul class="setting-list suboptions">
<template v-if="uploader === 'Pleroma.Uploaders.Local'">
<li>
<StringSetting
:path="[':pleroma','Pleroma.Uploaders.Local',':uploads']"
/>
</li>
</template>
<template v-else-if="uploader === 'Pleroma.Uploaders.IPFS'">
<li>
<StringSetting
:path="[':pleroma','Pleroma.Uploaders.IPFS',':get_gateway_url']"
/>
</li>
<li>
<StringSetting
:path="[':pleroma','Pleroma.Uploaders.IPFS',':post_gateway_url']"
/>
</li>
</template>
<template v-else-if="uploader === 'Pleroma.Uploaders.S3'">
<li>
<StringSetting
:path="[':pleroma','Pleroma.Uploaders.S3',':bucket']"
/>
</li>
<li>
<StringSetting
:path="[':pleroma','Pleroma.Uploaders.S3',':bucket_namespace']"
/>
</li>
<li>
<BooleanSetting
:path="[':pleroma','Pleroma.Uploaders.S3',':streaming_enabled']"
/>
</li>
<li>
<StringSetting
:path="[':pleroma','Pleroma.Uploaders.S3',':truncated_namespace']"
/>
</li>
</template>
<li>
<IntegerSetting
:path="[':pleroma','Pleroma.Uploaders.Uploader',':timeout']"
/>
</li>
</ul>
</li>
</ul>
<h3>{{ $t('admin_dash.uploads.attachments') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting
path=":pleroma.:instance.:attachment_links"
:options="uploaders"
/>
</li>
<li>
<BooleanSetting
path=":pleroma.:instance.:cleanup_attachments"
:options="uploaders"
/>
</li>
</ul>
<!-- CONFIRM how filters work -->
<h3>{{ $t('admin_dash.uploads.filenames') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting
:path="[':pleroma','Pleroma.Upload',':link_name']"
:subgroup="adapter"
/>
</li>
<li>
<IntegerSetting
:path="[':pleroma','Pleroma.Upload',':filename_display_max_length']"
:subgroup="adapter"
/>
</li>
<li>
<StringSetting
:path="[':pleroma','Pleroma.Upload',':default_description']"
:subgroup="adapter"
/>
</li>
<li>
<StringSetting
:path="[':pleroma','Pleroma.Upload',':base_url']"
:subgroup="adapter"
/>
</li>
<!-- TODO: add mime-type when we have a dynamic list component -->
</ul>
</div>
</div>
</template>
<script src="./uploads_tab.js"></script>

View file

@ -22,6 +22,7 @@
<slot v-else />
</span>
</Checkbox>
{{ ' ' }}
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"

View file

@ -9,6 +9,10 @@ export default {
},
props: {
...Setting.props,
overrideOptions: {
type: Boolean,
required: false
},
options: {
type: Array,
required: false
@ -22,7 +26,16 @@ export default {
computed: {
...Setting.computed,
realOptions () {
if (this.overrideOptions) {
return this.options
}
if (this.realSource === 'admin') {
if (
!this.backendDescriptionSuggestions?.length ||
this.backendDescriptionSuggestions?.length === 0
) {
return this.options
}
return this.backendDescriptionSuggestions.map(x => ({
key: x,
value: x,

View file

@ -2,6 +2,7 @@
<label
v-if="matchesExpertLevel"
class="ChoiceSetting"
:class="{ 'faint': shouldBeDisabled }"
>
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel }}
@ -11,8 +12,8 @@
</template>
{{ ' ' }}
<Select
:model-value="realDraftMode ? draft :state"
:disabled="disabled"
:model-value="realDraftMode ? draft : state"
:disabled="shouldBeDisabled"
@update:model-value="update"
>
<option

View file

@ -0,0 +1,17 @@
import Setting from './setting.js'
import ColorInput from 'src/components/color_input/color_input.vue'
export default {
...Setting,
components: {
...Setting.components,
ColorInput
},
methods: {
...Setting.methods,
getValue (e) {
console.log(e)
return e
}
}
}

View file

@ -0,0 +1,54 @@
<template>
<label
v-if="matchesExpertLevel"
class="ColorSetting"
>
<label
v-if="!hideLabel"
:for="path"
class="setting-label"
:class="{ 'faint': shouldBeDisabled }"
>
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel + ' ' }}
</template>
<template v-else-if="source === 'admin'">
MISSING LABEL FOR {{ path }}
</template>
<slot v-else />
</label>
{{ ' ' }}
<ColorInput
:id="path"
class="color-setting-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions"
:model-value="realDraftMode ? draft : state"
@update:model-value="update"
/>
{{ ' ' }}
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons v-if="!hideDraftButtons" />
<p
v-if="backendDescriptionDescription"
class="setting-description"
:class="{ 'faint': shouldBeDisabled }"
>
{{ backendDescriptionDescription + ' ' }}
</p>
</label>
</template>
<script src="./color_setting.js"></script>
<style lang="scss">
.ColorSetting {
.color-setting-input {
vertical-align: middle;
}
}
</style>

View file

@ -2,6 +2,7 @@
<!-- TODO make it reusable -->
<template>
<span
v-if="$parent.isDirty || $parent.canHardReset"
class="DraftButtons"
>
<Popover
@ -74,10 +75,9 @@ export default {
.DraftButtons {
display: inline-block;
position: relative;
.button-default {
margin-left: 0.5em;
}
display: inline-flex;
gap: 0.5em;
margin-top: 0.5em
}
.draft-tooltip {

View file

@ -1,13 +1,5 @@
import { isEqual } from 'lodash'
import Setting from './setting.js'
export default {
...Setting,
computed: {
...Setting.computed,
isDirty () {
return !isEqual(this.state, this.draft)
}
}
}

View file

@ -0,0 +1,133 @@
import Checkbox from 'src/components/checkbox/checkbox.vue'
import Setting from './setting.js'
export default {
...Setting,
data () {
return {
newValue: '',
}
},
components: {
...Setting.components,
Checkbox
},
props: {
...Setting.props,
ignoreSuggestions: {
required: false,
type: Boolean
},
overrideAvailableOptions: {
required: false,
type: Boolean
},
options: {
required: false,
type: Set
},
allowNew: {
required: false,
type: Boolean,
default: true
},
forceNew: {
required: false,
type: Boolean,
default: false
}
},
computed: {
...Setting.computed,
showNew () {
if (this.forceNew) return true
if (!this.allowNew) return false
const isExpert = this.$store.state.config.expertLevel > 0
const hasBuiltins = this.builtinEntries.length > 0
if (hasBuiltins) {
return isExpert
} else {
return true
}
},
valueSet () {
return new Set(this.visibleState)
},
suggestionsSet () {
const suggestions = this.backendDescriptionSuggestions
if (suggestions) {
return new Set(suggestions)
} else {
return new Set()
}
},
extraEntries () {
if (this.ignoreSuggestions) return [...this.valueSet.values()]
if (!this.suggestionsSet) return []
return [...this.valueSet.values()].filter((x) => {
return !this.builtinEntriesValueSet.has(x)
})
},
builtinEntries () {
if (this.ignoreSuggestions) return []
if (this.overrideAvailableOptions) {
return [...this.options]
}
if (!this.suggestionsSet) return []
const builtins = [...this.suggestionsSet.values()]
return builtins.map((option) => ({
label: option,
value: option
}))
},
builtinEntriesValueSet () {
return new Set(this.builtinEntries.map(x => x.value))
}
},
methods: {
...Setting.methods,
optionPresent (option) {
return this.valueSet.has(option)
},
getValue ({ event, value, index, eventType }) {
switch (eventType) {
case 'toggle': {
this.newValue = ''
const newSet = new Set(this.valueSet.values())
if (event) {
newSet.add(value)
} else {
newSet.delete(value)
}
return [...newSet.values()]
}
case 'add': {
if (!this.newValue) return this.valueSet.values()
const res = [...this.valueSet.values(), this.newValue]
this.newValue = ''
return res
}
case 'remove': {
const pre = [...this.valueSet.values()].slice(0, index)
const post = [...this.valueSet.values()].slice(index + 1)
return [...pre, ...post]
}
case 'edit': {
const pre = [...this.valueSet.values()].slice(0, index)
const post = [...this.valueSet.values()].slice(index + 1)
const string = event.target.value
if (!string) return [...this.valueSet.values()]
return [...pre, string, ...post]
}
}
}
}
}

View file

@ -0,0 +1,90 @@
<template>
<div
v-if="matchesExpertLevel"
class="ListSetting"
>
<label
class="setting-label"
:class="{ 'faint': shouldBeDisabled }"
>
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel + ' ' }}
</template>
<template v-else-if="source === 'admin'">
MISSING LABEL FOR {{ path }}
</template>
<slot v-else />
</label>
<p
v-if="backendDescriptionDescription"
class="setting-description"
:class="{ 'faint': shouldBeDisabled }"
>
{{ backendDescriptionDescription + ' ' }}
</p>
<ul class="setting-list">
<li
v-for="(item, i) in builtinEntries"
:key="i"
>
<Checkbox
:disabled="shouldBeDisabled"
:model-value="optionPresent(item.value)"
@update:model-value="event => update({ event, value: item.value, eventType: 'toggle' })"
>
{{ item.label }}
</Checkbox>
</li>
<li
v-for="(item, index) in extraEntries"
:key="index"
>
<div class="btn-group">
<input
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:value="item"
@change="e => update({ event: e, index, eventType: 'edit' })"
>
<button
class="button-default"
@click="e => update({ index, eventType: 'remove' })"
>
<FAIcon icon="times" />
</button>
</div>
</li>
<li v-if="showNew">
<div class="btn-group">
<input
v-model="newValue"
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
>
<button
class="button-default"
@click="e => update({ eventType: 'add' })"
>
<FAIcon icon="plus" />
</button>
</div>
</li>
</ul>
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons />
</div>
</template>
<script src="./list_setting.js"></script>
<style lang="scss">
.ListSetting {
.btn-group {
display: flex
}
}
</style>

View file

@ -0,0 +1,44 @@
import ListSetting from './list_setting.js'
export default {
...ListSetting,
data () {
return {
newValue: ['','']
}
},
methods: {
...ListSetting.methods,
getValue ({ event, index, eventType, tuple }) {
switch (eventType) {
case 'add': {
if (!this.newValue[0] || !this.newValue[1]) return this.visibleState
const res = [...this.visibleState, this.newValue]
this.newValue = ['', '']
return res
}
case 'remove': {
const pre = this.visibleState.slice(0, index)
const post = this.visibleState.slice(index + 1)
return [...pre, ...post]
}
case 'edit': {
const pre = this.visibleState.slice(0, index)
const post = this.visibleState.slice(index + 1)
const item = this.visibleState[index]
const string = event.target.value
if (!string) return this.visibleState
if (tuple === 0) {
return [...pre, [string, item[1]], ...post]
} else {
return [...pre, [item[0], string], ...post]
}
}
}
}
}
}

View file

@ -0,0 +1,113 @@
<template>
<div
v-if="matchesExpertLevel"
class="ListTupleSetting"
>
<label
class="setting-label"
:class="{ 'faint': shouldBeDisabled }"
>
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel + ' ' }}
</template>
<template v-else-if="source === 'admin'">
MISSING LABEL FOR {{ path }}
</template>
<slot v-else />
</label>
<p
v-if="backendDescriptionDescription"
class="setting-description"
:class="{ 'faint': shouldBeDisabled }"
>
{{ backendDescriptionDescription + ' ' }}
</p>
<ul class="setting-list">
<li
v-for="(item, index) in visibleState"
:key="index"
>
<div class="btn-group">
<input
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:value="item.tuple[0]"
@change="e => update({ event: e, index, eventType: 'edit', tuple: 0 })"
>
<input
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:value="item.tuple[1]"
@change="e => update({ event: e, index, eventType: 'edit', tuple: 1 })"
>
<button
class="button-default"
@click="e => update({ index, eventType: 'remove' })"
>
<FAIcon icon="times" />
</button>
</div>
</li>
<li>
<div class="btn-group">
<input
v-model="newValue[0]"
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions[0][0]"
>
<input
v-model="newValue[1]"
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions[0][1]"
>
<button
class="button-default"
@click="e => update({ eventType: 'add' })"
>
<FAIcon icon="plus" />
</button>
</div>
</li>
</ul>
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons />
</div>
</template>
<script src="./list_tuple_setting.js"></script>
<style lang="scss">
.ListTupleSetting {
.btn-group {
display: flex
}
dl {
display: inline-grid;
grid-template-columns: auto auto;
gap: 0.5em;
align-items: baseline;
dt {
display: inline;
font-weight: 800;
&::after {
content: ':'
}
}
dd {
display: inline;
margin: 0
}
}
}
</style>

View file

@ -0,0 +1,70 @@
import Setting from './setting.js'
export default {
...Setting,
props: {
...Setting.props,
allowNew: {
required: false,
type: Boolean,
default: true
}
},
data () {
return {
newValue: ['',''] // avoiding extra complexity by just using an array instead of an object
}
},
computed: {
...Setting.computed,
// state that we'll show in the UI, i.e. transforming map into list
displayState () {
return Object.entries(this.visibleState)
}
},
methods: {
...Setting.methods,
getValue ({ event, key, eventType, isKey }) {
switch (eventType) {
case 'add': {
if (key === '') return this.visibleState
const res = {...this.visibleState, ...Object.fromEntries([this.newValue])}
this.newValue = ['', '']
return res
}
case 'remove': {
// initial state for this type is empty array
if (Array.isArray(this.visibleState)) return this.visibleState
const newEntries = Object.entries(this.visibleState).filter(([k]) => k !== key)
if (newEntries.length === 0 ) return []
return Object.fromEntries(newEntries)
}
case 'edit': {
const string = event.target.value
const newEntries = Object
.entries(this.visibleState)
.map(([k, v]) => {
if (isKey) {
if (k === key) {
return [string, v]
} else {
return [k, v]
}
} else {
if (k === key) {
return [k, string]
} else {
return [k, v]
}
}
})
return Object.fromEntries(newEntries)
}
}
}
}
}

View file

@ -0,0 +1,112 @@
<template>
<div
v-if="matchesExpertLevel"
class="MapSetting"
>
<label
class="setting-label"
:class="{ 'faint': shouldBeDisabled }"
>
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel + ' ' }}
</template>
<template v-else-if="source === 'admin'">
MISSING LABEL FOR {{ path }}
</template>
<slot v-else />
</label>
<p
v-if="backendDescriptionDescription"
class="setting-description"
:class="{ 'faint': shouldBeDisabled }"
>
{{ backendDescriptionDescription + ' ' }}
</p>
<ul class="setting-list">
<li
v-for="(item, i) in displayState"
:key="i"
>
<div class="btn-group">
<input
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:value="item[0]"
@change="e => update({ event: e, key: item[0], eventType: 'edit', isKey: true })"
>
<input
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:value="item[1]"
@change="e => update({ event: e, key: item[0], eventType: 'edit', isKey: false })"
>
<button
class="button-default"
@click="e => update({ key: item[0], eventType: 'remove' })"
>
<FAIcon icon="times" />
</button>
</div>
</li>
<li v-if="allowNew">
<div class="btn-group">
<input
v-model="newValue[0]"
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions[0][0]"
>
<input
v-model="newValue[1]"
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions[0][1]"
>
<button
class="button-default"
@click="e => update({ eventType: 'add' })"
>
<FAIcon icon="plus" />
</button>
</div>
</li>
</ul>
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons />
</div>
</template>
<script src="./map_setting.js"></script>
<style lang="scss">
.ListSetting {
.btn-group {
display: flex
}
dl {
display: inline-grid;
grid-template-columns: auto auto;
gap: 0.5em;
dt {
display: inline;
font-weight: 800;
&::after {
content: ':'
}
}
dd {
display: inline;
margin: 0
}
}
}
</style>

View file

@ -4,7 +4,9 @@
class="NumberSetting"
>
<label
v-if="!hideLabel"
:for="path"
class="setting-label"
:class="{ 'faint': shouldBeDisabled }"
>
<template v-if="backendDescriptionLabel">
@ -22,6 +24,7 @@
type="number"
:step="step || 1"
:disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions"
:min="min || 0"
:value="realDraftMode ? draft :state"
@change="update"
@ -32,7 +35,7 @@
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons />
<DraftButtons v-if="!hideDraftButtons" />
<p
v-if="backendDescriptionDescription"
class="setting-description"

View file

@ -0,0 +1,53 @@
import Checkbox from 'src/components/checkbox/checkbox.vue'
import Setting from './setting.js'
const getUrl = state => state?.tuple ? state.tuple[1] + ':' + state.tuple[2] : state
const getSocks = state => state?.tuple
export default {
...Setting,
data () {
return {
urlField: '',
socksField: false
}
},
created () {
Setting.created()
this.urlField = getUrl(this.realDraftMode ? this.draft : this.state)
this.socksField = getSocks(this.realDraftMode ? this.draft : this.state)
},
computed: {
...Setting.computed,
// state that we'll show in the UI, i.e. transforming map into list
displayState () {
if (this.visibleState?.tuple) {
return this.visibleState.tuple[1] + ':' + this.visibleState.tuple[2]
}
return this.visibleState
},
socksState () {
return getSocks(this.visibleState)
}
},
components: {
...Setting.components,
Checkbox
},
methods: {
...Setting.methods,
getValue ({ event, isProxy}) {
if (isProxy) {
this.socksField = event
} else {
this.urlField = event.target.value
}
if (this.socksField) {
return { tuple: [ ':socks5', ...this.urlField.split(':') ] }
} else {
return this.urlField
}
}
}
}

View file

@ -0,0 +1,57 @@
<template>
<label
v-if="matchesExpertLevel"
class="ProxySetting"
>
<label
v-if="!hideLabel"
:for="path"
class="setting-label"
:class="{ 'faint': shouldBeDisabled }"
>
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel + ' ' }}
</template>
<template v-else-if="source === 'admin'">
MISSING LABEL FOR {{ path }}
</template>
<slot v-else />
</label>
{{ ' ' }}
<input
:id="path"
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions[0]"
:value="displayState"
@change="event => update({ event })"
>
{{ ' ' }}
<Checkbox
:model-value="socksState"
:disabled="shouldBeDisabled"
:indeterminate="isIndeterminate"
@update:model-value="event => update({ event, isProxy: true})"
>
{{ $t('admin_dash.http.socks5') }}
{{ ' ' }}
</Checkbox>
{{ ' ' }}
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons v-if="!hideDraftButtons" />
<p
v-if="backendDescriptionDescription"
class="setting-description"
:class="{ 'faint': shouldBeDisabled }"
>
{{ backendDescriptionDescription + ' ' }}
</p>
</label>
</template>
<script src="./proxy_setting.js"></script>

View file

@ -0,0 +1,45 @@
import { clone } from 'lodash'
import Setting from './setting.js'
export default {
...Setting,
methods: {
...Setting.methods,
optionPresent (option) {
return this.valueSet.has(option)
},
getValue ({ event, field, index, eventType }) {
switch (eventType) {
case 'add': {
const res = [...this.visibleState, {}]
return res
}
case 'remove': {
const pre = this.visibleState.slice(0, index)
const post = this.visibleState.slice(index + 1)
return [...pre, ...post]
}
case 'edit': {
const pre = this.visibleState.slice(0, index)
const post = this.visibleState.slice(index + 1)
const item = clone(this.visibleState[index])
const string = event.target.value
console.log(item)
if (!string) {
delete item[field]
} else {
item[field] = string
}
console.log(item)
return [...pre, item, ...post]
}
}
}
}
}

View file

@ -0,0 +1,138 @@
<template>
<div
v-if="matchesExpertLevel"
class="PWAManifestIconsSetting"
>
<label
class="setting-label"
:class="{ 'faint': shouldBeDisabled }"
>
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel + ' ' }}
</template>
<template v-else-if="source === 'admin'">
MISSING LABEL FOR {{ path }}
</template>
<slot v-else />
</label>
<p
v-if="backendDescriptionDescription"
class="setting-description"
:class="{ 'faint': shouldBeDisabled }"
>
{{ backendDescriptionDescription + ' ' }}
</p>
<ul class="setting-list">
<li
v-for="(item, index) in visibleState"
:key="index"
>
<div>
<dl>
<dt><code>purpose</code></dt>
<dd>
<input
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:value="item[':purpose']"
@change="e => update({ event: e, index, eventType: 'edit', field: ':purpose' })"
>
</dd>
<dt><code>sizes</code></dt>
<dd>
<input
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:value="item[':sizes']"
@change="e => update({ event: e, index, eventType: 'edit', field: ':sizes' })"
>
</dd>
<dt><code>src</code></dt>
<dd>
<input
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:value="item[':src']"
@change="e => update({ event: e, index, eventType: 'edit', field: ':src' })"
>
</dd>
<dt><code>type</code></dt>
<dd>
<input
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:value="item[':type']"
@change="e => update({ event: e, index, eventType: 'edit', field: ':type' })"
>
</dd>
</dl>
<div class="buttons">
<button
v-if="index === visibleState.length - 1"
class="button-default add-button"
@click="e => update({ eventType: 'add' })"
>
<FAIcon icon="plus" />
</button>
<button
class="button-default delete-button"
@click="e => update({ index, eventType: 'remove' })"
>
<FAIcon icon="times" />
</button>
</div>
</div>
</li>
</ul>
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons />
</div>
</template>
<script src="./pwa_manifest_icons_setting.js"></script>
<style lang="scss">
.PWAManifestIconsSetting {
display: inline-block;
.setting-list {
display: grid;
gap: 0.5em;
}
.buttons {
display: flex;
gap: 0.5em;
justify-content: right;
margin-top: 0.5em;
button {
line-height: 2;
}
}
dl {
display: inline-grid;
grid-template-columns: auto auto;
gap: 0.5em;
align-items: baseline;
dt {
display: inline;
font-weight: 800;
text-align: right;
&::after {
content: ':'
}
}
dd {
display: inline;
margin: 0
}
}
}
</style>

View file

@ -1,7 +1,7 @@
import ModifiedIndicator from './modified_indicator.vue'
import ProfileSettingIndicator from './profile_setting_indicator.vue'
import DraftButtons from './draft_buttons.vue'
import { get, set, cloneDeep } from 'lodash'
import { get, set, cloneDeep, isEqual } from 'lodash'
export default {
components: {
@ -18,6 +18,18 @@ export default {
type: [String, Array],
required: false
},
descriptionPathOverride: {
type: [String, Array],
required: false
},
suggestions: {
type: [String, Array],
required: false
},
subgroup: {
type: String,
required: false
},
disabled: {
type: Boolean,
default: false
@ -37,12 +49,22 @@ export default {
type: String,
default: undefined
},
hideDraftButtons: { // this is for the weird backend hybrid (Boolean|String or Boolean|Number) settings
required: false,
type: Boolean
},
hideLabel: {
type: Boolean
},
hideDescription: {
type: Boolean
},
swapDescriptionAndLabel: {
type: Boolean
},
backendDescriptionPath: {
type: [String, Array]
},
overrideBackendDescription: {
type: Boolean
},
@ -73,7 +95,7 @@ export default {
},
created () {
if (this.realDraftMode && (this.realSource !== 'admin' || this.path == null)) {
this.draft = this.state
this.draft = cloneDeep(this.state)
}
},
computed: {
@ -114,10 +136,13 @@ export default {
return typeof this.draftMode === 'undefined' ? this.defaultDraftMode : this.draftMode
},
backendDescription () {
return get(this.$store.state.adminSettings.descriptions, this.path)
return get(this.$store.state.adminSettings.descriptions, this.descriptionPath)
},
backendDescriptionLabel () {
if (this.realSource !== 'admin') return ''
if (this.overrideBackendDescriptionLabel !== '' && typeof this.overrideBackendDescriptionLabel === 'string') {
return this.overrideBackendDescriptionLabel
}
if (!this.backendDescription || this.overrideBackendDescriptionLabel) {
return this.$t([
'admin_dash',
@ -148,13 +173,20 @@ export default {
}
},
backendDescriptionSuggestions () {
return this.backendDescription?.suggestions
return this.backendDescription?.suggestions || this.suggestions
},
shouldBeDisabled () {
if (this.path == null) {
return this.disabled
}
const parentValue = this.parentPath !== undefined ? get(this.configSource, this.parentPath) : null
let parentValue = null
if (this.parentPath !== undefined && this.realSource === 'admin') {
if (this.realDraftMode) {
parentValue = get(this.$store.state.adminSettings.draft, this.parentPath)
} else {
parentValue = get(this.configSource, this.parentPath)
}
}
return this.disabled || (parentValue !== null ? (this.parentInvert ? parentValue : !parentValue) : false)
},
configSource () {
@ -209,17 +241,29 @@ export default {
if (this.path == null) return null
return Array.isArray(this.path) ? this.path : this.path.split('.')
},
descriptionPath () {
if (this.path == null) return null
if (this.descriptionPathOverride) return this.descriptionPathOverride
const path = Array.isArray(this.path) ? this.path : this.path.split('.')
if (this.subgroup) {
return [
...path.slice(0, path.length - 1),
':subgroup,' + this.subgroup,
...path.slice(path.length - 1)
]
}
return path
},
isDirty () {
if (this.path == null) return false
if (this.realSource === 'admin' && this.canonPath.length > 3) {
return false // should not show draft buttons for "grouped" values
} else {
return this.realDraftMode && this.draft !== this.state
return this.realDraftMode && !isEqual(this.draft, this.state)
}
},
canHardReset () {
return this.realSource === 'admin' && this.$store.state.adminSettings.modifiedPaths &&
this.$store.state.adminSettings.modifiedPaths.has(this.canonPath.join(' -> '))
return this.realSource === 'admin' && this.$store.state.adminSettings.modifiedPaths?.has(this.canonPath.join(' -> '))
},
matchesExpertLevel () {
return (this.expert || 0) <= this.$store.state.config.expertLevel > 0

View file

@ -4,6 +4,7 @@
class="StringSetting"
>
<label
v-if="!hideLabel"
:for="path"
class="setting-label"
:class="{ 'faint': shouldBeDisabled }"
@ -20,7 +21,9 @@
<input
:id="path"
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions"
:value="realDraftMode ? draft : state"
@change="update"
>
@ -30,7 +33,7 @@
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons />
<DraftButtons v-if="!hideDraftButtons" />
<p
v-if="backendDescriptionDescription"
class="setting-description"

View file

@ -0,0 +1,16 @@
import Setting from './setting.js'
export default {
...Setting,
methods: {
...Setting.methods,
getValue ({ e, side }) {
const [a, b] = this.visibleState || []
if (side === 0) {
return { tuple: [e.target.value, b]}
} else {
return { tuple: [a, e.target.value]}
}
}
}
}

View file

@ -0,0 +1,67 @@
<template>
<label
v-if="matchesExpertLevel"
class="TupleSetting"
>
<label
v-if="!hideLabel"
:for="path"
class="setting-label"
:class="{ 'faint': shouldBeDisabled }"
>
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel + ' ' }}
</template>
<template v-else-if="source === 'admin'">
MISSING LABEL FOR {{ path }}
</template>
<slot v-else />
</label>
{{ ' ' }}
<input
:id="path"
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions?.[0]?.[0]"
:value="visibleState?.tuple?.[0]"
@change="e => update({ e, side: 0 })"
>
{{ ' ' }}
<input
:id="path"
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions?.[0]?.[1]"
:value="visibleState?.tuple?.[1]"
@change="e => update({ e, side: 1 })"
>
{{ ' ' }}
<input
:id="path"
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions?.[0]?.[1]"
:value="visibleState?.tuple?.[2]"
@change="e => update({ e, side: 2 })"
>
{{ ' ' }}
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons v-if="!hideDraftButtons" />
<p
v-if="backendDescriptionDescription"
class="setting-description"
:class="{ 'faint': shouldBeDisabled }"
>
{{ backendDescriptionDescription + ' ' }}
</p>
</label>
</template>
<script src="./tuple_setting.js"></script>

View file

@ -159,10 +159,10 @@ export default {
const wrapperClasses = ['tab-content-wrapper', active ? '-active' : '-hidden' ]
const contentClasses = ['tab-content']
if (props['full-width']) {
if (props['full-width'] || props['full-width'] === '') {
contentClasses.push('-full-width')
}
if (props['full-height']) {
if (props['full-height'] || props['full-width'] === '') {
contentClasses.push('-full-height')
}
return (

View file

@ -31,6 +31,14 @@
margin-top: 0;
}
p {
line-height: 1.5;
}
.setting-item > p {
margin-left: 1em;
}
.setting-list,
.option-list {
list-style-type: none;
@ -41,8 +49,21 @@
padding: 0 2em;
}
.btn-group {
.button-default {
flex: 0 1 auto;
}
}
li {
margin: 1em 0;
display: block;
margin: 0.75em 0;
> label {
display: block;
margin: 0.75em 0;
padding: 0.5em 0;
}
}
.suboptions {
@ -62,8 +83,8 @@
.setting-description {
margin-top: 0.2em;
margin-bottom: 2em;
font-size: 70%;
margin-bottom: 0;
font-size: 80%;
}
.settings-modal-panel {

View file

@ -1,32 +1,58 @@
import VerticalTabSwitcher from './helpers/vertical_tab_switcher.jsx'
import InstanceTab from './admin_tabs/instance_tab.vue'
import LinksTab from './admin_tabs/links_tab.vue'
import LimitsTab from './admin_tabs/limits_tab.vue'
import FrontendsTab from './admin_tabs/frontends_tab.vue'
import MediaProxyTab from './admin_tabs/media_proxy_tab.vue'
import EmojiTab from './admin_tabs/emoji_tab.vue'
import UploadsTab from './admin_tabs/uploads_tab.vue'
import MailerTab from './admin_tabs/mailer_tab.vue'
import MonitoringTab from './admin_tabs/monitoring_tab.vue'
import RegistrationsTab from './admin_tabs/registrations_tab.vue'
import AuthTab from './admin_tabs/auth_tab.vue'
import HTTPTab from './admin_tabs/http_tab.vue'
import OtherTab from './admin_tabs/other_tab.vue'
import PostsTab from './admin_tabs/posts_tab.vue'
import FederationTab from './admin_tabs/federation_tab.vue'
import JobQueuesTab from './admin_tabs/job_queues_tab.vue'
import { useInterfaceStore } from 'src/stores/interface'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faWrench,
faHand,
faChain,
faGlobe,
faLaptopCode,
faPaintBrush,
faBell,
faDownload,
faEyeSlash,
faInfo
faTowerBroadcast,
faEnvelope,
faChartLine,
faDoorOpen,
faGears,
faKey,
faCircleNodes,
faUpload,
faMessage,
faEllipsis
} from '@fortawesome/free-solid-svg-icons'
library.add(
faWrench,
faHand,
faChain,
faGlobe,
faLaptopCode,
faPaintBrush,
faBell,
faDownload,
faEyeSlash,
faInfo
faTowerBroadcast,
faEnvelope,
faChartLine,
faDoorOpen,
faGears,
faKey,
faCircleNodes,
faUpload,
faMessage,
faEllipsis
)
const SettingsModalAdminContent = {
@ -34,9 +60,21 @@ const SettingsModalAdminContent = {
VerticalTabSwitcher,
InstanceTab,
LimitsTab,
RegistrationsTab,
EmojiTab,
FrontendsTab,
EmojiTab
FederationTab,
LimitsTab,
MailerTab,
UploadsTab,
MediaProxyTab,
LinksTab,
JobQueuesTab,
AuthTab,
HTTPTab,
MonitoringTab,
OtherTab,
PostsTab
},
computed: {
user () {

View file

@ -48,6 +48,41 @@
>
<InstanceTab />
</div>
<div
:label="$t('admin_dash.tabs.registrations')"
icon="door-open"
data-tab-name="registrations"
>
<RegistrationsTab />
</div>
<div
:label="$t('admin_dash.tabs.auth')"
icon="key"
data-tab-name="monitoring"
>
<AuthTab />
</div>
<div
:label="$t('admin_dash.tabs.emoji')"
icon="face-smile-beam"
data-tab-name="emoji"
full-width
>
<EmojiTab />
</div>
<div
:label="$t('admin_dash.tabs.frontends')"
icon="laptop-code"
data-tab-name="frontends"
full-width
>
<FrontendsTab />
</div>
<div
v-if="adminDbLoaded"
:label="$t('admin_dash.tabs.limits')"
@ -56,20 +91,85 @@
>
<LimitsTab />
</div>
<div
:label="$t('admin_dash.tabs.frontends')"
icon="laptop-code"
data-tab-name="frontends"
:label="$t('admin_dash.tabs.uploads')"
icon="upload"
data-tab-name="uploads"
>
<FrontendsTab />
<UploadsTab />
</div>
<div
:label="$t('admin_dash.tabs.emoji')"
icon="face-smile-beam"
data-tab-name="emoji"
:label="$t('admin_dash.tabs.media_proxy')"
icon="tower-broadcast"
data-tab-name="media_proxy"
>
<EmojiTab />
<MediaProxyTab />
</div>
<div
:label="$t('admin_dash.tabs.posts')"
icon="message"
data-tab-name="other"
>
<PostsTab />
</div>
<div
:label="$t('admin_dash.tabs.links')"
icon="chain"
data-tab-name="links"
>
<LinksTab />
</div>
<div
:label="$t('admin_dash.tabs.mailer')"
icon="envelope"
data-tab-name="mailer"
>
<MailerTab />
</div>
<div
:label="$t('admin_dash.tabs.federation')"
icon="circle-nodes"
data-tab-name="monitoring"
>
<FederationTab />
</div>
<div
:label="$t('admin_dash.tabs.http')"
icon="globe"
data-tab-name="http"
>
<HTTPTab />
</div>
<div
:label="$t('admin_dash.tabs.job_queues')"
icon="gears"
data-tab-name="job_queues"
>
<JobQueuesTab />
</div>
<div
:label="$t('admin_dash.tabs.monitoring')"
icon="chart-line"
data-tab-name="monitoring"
>
<MonitoringTab />
</div>
<div
:label="$t('admin_dash.tabs.other')"
icon="ellipsis"
data-tab-name="other"
>
<OtherTab />
</div>
</vertical-tab-switcher>
</template>

View file

@ -161,13 +161,16 @@
<div class="fun-monitor-neck button-default" />
<div class="fun-monitor-display-bezel button-default">
<div class="fun-monitor-display-screen input">
<img
v-if="backgroundPreview || user.background_image || instanceWallpaper"
class="fun-monitor-display-screen-image"
:src="backgroundPreview || user.background_image || instanceWallpaper"
/>
<div v-else class="wallpaper" />
<div class="fun-monitor-display-screen-overlay input" />
<img
v-if="backgroundPreview || user.background_image || instanceWallpaper"
class="fun-monitor-display-screen-image"
:src="backgroundPreview || user.background_image || instanceWallpaper"
>
<div
v-else
class="wallpaper"
/>
<div class="fun-monitor-display-screen-overlay input" />
<div
v-if="backgroundUploading"
class="fun-monitor-display-uploading"

View file

@ -217,7 +217,6 @@ const FilteringTab = {
},
purgeExpiredFilters () {
this.muteFiltersExpired.forEach(([id]) => {
console.log(id)
delete this.muteFiltersDraftObject[id]
this.unsetPreference({ path: 'simple.muteFilters.' + id , value: null })
})

View file

@ -0,0 +1,26 @@
<template>
<div :label="$t('admin_dash.tabs.uploads')">
<div class="setting-item">
<h3>{{ $t('admin_dash.uploads.general') }}</h3>
<ul class="setting-list">
<li>
<ChoiceSetting
:path="[':pleroma','Pleroma.Upload']"
:option-label-map="adaptersLabels"
/>
</li>
<h4>{{ $t('admin_dash.mailer.auth') }}</h4>
<li>
<!-- authentication info -->
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':api_key']"
:password="true"
:subgroup="adapter"
/>
</li>
</ul>
</div>
</div>
</template>
<script src="./uploads_tab.js"></script>

View file

@ -1152,7 +1152,80 @@
"instance": "Instance",
"limits": "Limits",
"frontends": "Front-ends",
"emoji": "Emoji"
"mailer": "EMails",
"media_proxy": "Media Proxy",
"emoji": "Emoji",
"uploads": "Uploads",
"monitoring": "Monitoring",
"registrations": "Registrations",
"links": "Links",
"job_queues": "Job Queues",
"auth": "Auth",
"posts": "Posts",
"http": "HTTP",
"federation": "Federation",
"other": "Other"
},
"posts": {
"global": "Global settings",
"local": "Local posts",
"scheduled_activites": "Scheduled posts",
"remote": "Remote posts"
},
"other": {
"uncategorized": "Uncategorized",
"user_backup": "User Backup",
"reports": "Reports",
"feed": "RSS Feed",
"feed_title": "Article Title",
"mime": "MIME Types",
"remote_ip": "Reverse Proxy / Remote IP",
"remote_ip_description": "This should be disabled only if your instance is NOT behind a reverse proxy",
"streamer": "Notifications Streamer",
"privileges": "Privileges"
},
"monitoring": {
"builtins": "Built-in Tools",
"prometheus": "Prometheus Exporter"
},
"federation": {
"global": "Global settings",
"restrictions": "Restrictions",
"activitypub": "ActivityPub"
},
"http": {
"outbound": "Outbound HTTP requests",
"socks5": "SOCKS5"
},
"auth": {
"MFA": "Multi-factor Authentication",
"LDAP": "LDAP Settings",
"OAuth": "Oauth2 settings",
"TOTP": "One-time Passwords (TOTP)",
"backup_codes": "Backup codes"
},
"job_queues": {
"Oban": "Oban queues",
"Gun": {
"title": "Gun queues",
"connections_pools": "Gun connections pool",
"pools": {
"title": "Gun worker pools",
"default": "Default pool",
"federation": "Federation pool",
"media": "Media pool",
"rich_media": "Rich media pool",
"upload": "Upload pool"
}
},
"Hackney": {
"title": "Hackney pools",
"federation": "Federation",
"media": "Media",
"rich_media": "Rich media",
"upload": "Upload"
},
"queues": "Queues"
},
"nodb": {
"heading": "Database config is disabled",
@ -1164,29 +1237,79 @@
"native": "Native",
"kocaptcha": "KoCaptcha"
},
"http": {
"outbound": "Outgoing connections",
"incoming": "Incoming connections",
"security": "HTTP Security",
"cors": "Cross-origin Resource Sharing (CORS)",
"web_cache_ttl": "Web Cache TTL",
"web_cache_ttl_description": "Amount of milliseconds until web response cache is cleared. Use `nil` to disable expiration entirely.",
"web_push": "Web Push",
"web_push_description": "Web Push VAPID settings. You can use the mix task web_push.gen.keypair to generate it."
},
"instance": {
"instance": "Instance information",
"branding": "Branding",
"registrations": "User sign-ups",
"captcha_header": "CAPTCHA",
"kocaptcha": "KoCaptcha settings",
"access": "Instance access",
"rich_metadata": "Metadata",
"restrict": {
"header": "Restrict access for anonymous visitors",
"description": "Detailed setting for allowing/disallowing access to certain aspects of API. By default (indeterminate state) it will disallow if instance is not public, ticked checkbox means disallow access even if instance is public, unticked means allow access even if instance is private. Please note that unexpected behavior might happen if some settings are set, i.e. if profile access is disabled posts will show without profile information.",
"timelines": "Timelines access",
"profiles": "User profiles access",
"activities": "Statuses/activities access"
}
},
":unauthenticated": "Unauthenticated",
":all": "Everyone"
},
"registrations": {
"welcome": {
"title": "Welcome message",
"description": "Send new users a message when they sign up",
"direct_message": "Via direct message",
"chat_message": "Via chat",
"email_message": "Via email"
},
"restrictions": "Restrictions",
"autofollow": "Autofollow"
},
"links": {
"no_scheme": "No scheme",
"link_previews": "Link previews",
"link_formatter": "Link formatter"
},
"uploads": {
"attachments": "Attachments settings",
"upload": "Upload",
"local_uploader": "Local files",
"filenames": "Filenames, Titles and Descriptions",
"uploader_settings": "Uploader settings"
},
"media_proxy": {
"basic": "Basic Settings",
"invalidation": "Cache Invalidation",
"limits": "Limits",
"thumbnails": "Thumbnail Generation",
"invalidation_settings": "Cache Invalidation"
},
"mailer": {
"adapter": "Mailing Adapter",
"auth": "Authentication"
},
"limits": {
"arbitrary_limits": "Arbitrary limits",
"posts": "Post limits",
"other": "Misc. limits",
"uploads": "Attachments limits",
"users": "User profile limits",
"profile_fields": "Profile fields limits",
"user_uploads": "Profile media limits"
},
"frontend": {
"title": "Frontend management",
"repository": "Repository link",
"versions": "Available versions",
"build_url": "Build URL",
@ -1210,7 +1333,11 @@
"emoji": {
"global_actions": "Global actions",
"reload": "Reload emoji",
"reload_short": "Refresh",
"advanced": "Advanced",
"importFS": "Import emoji from filesystem",
"import_pack": "Upload emoji pack",
"import_pack_short": "Import",
"error": "Error: {0}",
"create_pack": "Create pack",
"delete_pack": "Delete pack",
@ -1218,10 +1345,12 @@
"create": "Create",
"emoji_packs": "Emoji packs",
"remote_packs": "Remote packs",
"remote_packs_short": "Remote",
"do_list": "List",
"remote_pack_instance": "Remote pack instance",
"emoji_pack": "Emoji pack",
"edit_pack": "Edit pack",
"metadata": "Metadata",
"description": "Description",
"homepage": "Homepage",
"fallback_src": "Fallback source",
@ -1257,8 +1386,67 @@
"replace_warning": "This will REPLACE the local pack of the same name",
"copied_successfully": "Successfully copied emoji \"{0}\" to pack \"{1}\""
},
"generic_enforcement": {
"if_available": "If available",
"always": "Always",
"never": "Never"
},
"temp_overrides": {
":pleroma": {
"Oban": {
":queues": {
":scheduled_activites": {
"label": "Scheduled activities",
"description": "Scheduled activites queue"
},
":slow": {
"label": "Slow",
"description": "idk"
}
}
},
":connections_pool": {
":max_idle_time": {
"label": "Maximum idle time",
"description": "idk"
},
":retry": {
"label": "Retry",
"description": "idk"
}
},
":pools": {
":rich_media": {
"label": "Rich media",
"description": "idk"
}
},
"Pleroma_DOT_Formatter": {
":attribute_toggle": {
"label": "Set {attr} attribute"
},
":truncate_toggle": {
"label": "Truncate"
},
":class": {
"label": "Value"
},
":rel": {
"label": "Value"
}
},
"Pleroma_DOT_Uploaders_DOT_Uploader": {
":timeout": {
"label": "Timeout",
"description": "Amount of milliseconds before dropping connection"
}
},
"Pleroma_DOT_Upload": {
":default_description": {
"label": "Default description",
"description": "Default description to give to a file. Setting it to ':filename' will use file's filename as description."
}
},
":instance": {
":public": {
"label": "Instance is public",
@ -1276,6 +1464,16 @@
"label": "Background image",
"description": "Background image (primarily used by PleromaFE)"
}
},
":http_security": {
":allow_unsafe_eval": {
"label": "Allow unsafe-eval",
"description": "Allow unsafe evaluation of scripts (required for Flash support)"
},
":report_url": {
"label": "Report URL",
"description": "URL to report security violations to"
}
}
}
}

View file

@ -105,6 +105,16 @@ const adminSettingsStorage = {
}
set(config, path, convert(c.value))
})
console.log('CONFIG', JSON.parse(JSON.stringify(config)))
// patching http adapter config to be easier to handle
const adapter = config[':pleroma'][':http'][':adapter']
if (Array.isArray(adapter)) {
config[':pleroma'][':http'][':adapter'] = {
[':ssl_options']: {
[':versions']: []
}
}
}
commit('updateAdminSettings', { config, modifiedPaths })
commit('resetAdminDraft')
},
@ -121,7 +131,9 @@ const adminSettingsStorage = {
}
const descriptions = {}
backendDescriptions.forEach(d => convert(d, '', descriptions))
console.log('DESCRIPTIONS', descriptions)
commit('updateAdminDescriptions', { descriptions })
},
@ -206,7 +218,7 @@ const adminSettingsStorage = {
.then(backendDbConfig => dispatch('setInstanceAdminSettings', { backendDbConfig }))
},
resetAdminSetting ({ rootState, state, dispatch }, { path }) {
const [group, key, subkey] = path.split(/\./g)
const [group, key, subkey] = Array.isArray(path) ? path : path.split(/\./g)
state.modifiedPaths.delete(path)