Merge branch 'improve_settings_reusability' into shigusegubu-vue3
* improve_settings_reusability: better frontends tab, now you can set default frontend frontends tab initial implementation, now you can (re)install frontends! yay! fix handle db config disabled case localization strings added mass-draft-push and mass-draft-reset, small stylistic fixes
This commit is contained in:
commit
85c79f666c
16 changed files with 530 additions and 27 deletions
23
src/App.scss
23
src/App.scss
|
@ -645,6 +645,20 @@ option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cards-list {
|
||||||
|
list-style: none;
|
||||||
|
display: grid;
|
||||||
|
grid-auto-flow: row dense;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
|
||||||
|
li {
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--inputRadius);
|
||||||
|
padding: 0.5em;
|
||||||
|
margin: 0.25em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.btn-block {
|
.btn-block {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -655,16 +669,19 @@ option {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
||||||
button {
|
button,
|
||||||
|
.button-dropdown {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
|
||||||
&:not(:last-child) {
|
&:not(:last-child),
|
||||||
|
&:not(:last-child) .button-default {
|
||||||
border-top-right-radius: 0;
|
border-top-right-radius: 0;
|
||||||
border-bottom-right-radius: 0;
|
border-bottom-right-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(:first-child) {
|
&:not(:first-child),
|
||||||
|
&:not(:first-child) .button-default {
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
border-bottom-left-radius: 0;
|
border-bottom-left-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
64
src/components/settings_modal/admin_tabs/frontends_tab.js
Normal file
64
src/components/settings_modal/admin_tabs/frontends_tab.js
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
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 Popover from 'src/components/popover/popover.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 FrontendsTab = {
|
||||||
|
provide () {
|
||||||
|
return {
|
||||||
|
defaultDraftMode: true,
|
||||||
|
defaultSource: 'admin'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
BooleanSetting,
|
||||||
|
ChoiceSetting,
|
||||||
|
IntegerSetting,
|
||||||
|
StringSetting,
|
||||||
|
GroupSetting,
|
||||||
|
Popover
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
if (this.user.rights.admin) {
|
||||||
|
this.$store.dispatch('loadFrontendsStuff')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
frontends () {
|
||||||
|
return this.$store.state.adminSettings.frontends
|
||||||
|
},
|
||||||
|
...SharedComputedObject()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
update (frontend, suggestRef) {
|
||||||
|
const ref = suggestRef || frontend.refs[0]
|
||||||
|
const { name } = frontend
|
||||||
|
const payload = { name, ref }
|
||||||
|
|
||||||
|
this.$store.state.api.backendInteractor.installFrontend({ payload })
|
||||||
|
.then((externalUser) => {
|
||||||
|
this.$store.dispatch('loadFrontendsStuff')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
setDefault (frontend, suggestRef) {
|
||||||
|
const ref = suggestRef || frontend.refs[0]
|
||||||
|
const { name } = frontend
|
||||||
|
|
||||||
|
this.$store.commit('updateAdminDraft', { path: [':pleroma', ':frontends', ':primary'], value: { name, ref } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FrontendsTab
|
13
src/components/settings_modal/admin_tabs/frontends_tab.scss
Normal file
13
src/components/settings_modal/admin_tabs/frontends_tab.scss
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
.frontends-tab {
|
||||||
|
.cards-list {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
word-wrap: nowrap;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow-x: hidden;
|
||||||
|
max-width: 10em;
|
||||||
|
}
|
||||||
|
}
|
162
src/components/settings_modal/admin_tabs/frontends_tab.vue
Normal file
162
src/components/settings_modal/admin_tabs/frontends_tab.vue
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
<template>
|
||||||
|
<div class="frontends-tab" :label="$t('admin_dash.tabs.frontends')">
|
||||||
|
<div class="setting-item">
|
||||||
|
<h2>{{ $t('admin_dash.tabs.frontends') }}</h2>
|
||||||
|
<p>{{ $t('admin_dash.frontend.wip_notice') }}</p>
|
||||||
|
<ul class="setting-list">
|
||||||
|
<li>
|
||||||
|
<h3>{{ $t('admin_dash.frontend.default_frontend') }}</h3>
|
||||||
|
<p>{{ $t('admin_dash.frontend.default_frontend_tip') }}</p>
|
||||||
|
<p>{{ $t('admin_dash.frontend.default_frontend_tip2') }}</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<StringSetting path=":pleroma.:frontends.:primary.name">
|
||||||
|
NAME
|
||||||
|
</StringSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<StringSetting path=":pleroma.:frontends.:primary.ref">
|
||||||
|
REF
|
||||||
|
</StringSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<GroupSetting path=":pleroma.:frontends.:primary"/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="setting-list">
|
||||||
|
<h3>{{ $t('admin_dash.frontend.available_frontends') }}</h3>
|
||||||
|
<ul class="cards-list">
|
||||||
|
<li v-for="frontend in frontends" :key="frontend.name">
|
||||||
|
<strong>{{ frontend.name }}</strong>
|
||||||
|
{{ ' ' }}
|
||||||
|
<span v-if="adminDraft[':pleroma'][':frontends'][':primary'].name === frontend.name">
|
||||||
|
<i18n-t
|
||||||
|
keypath="admin_dash.frontend.is_default"
|
||||||
|
v-if="adminDraft[':pleroma'][':frontends'][':primary'].ref === frontend.refs[0]"
|
||||||
|
/>
|
||||||
|
<i18n-t
|
||||||
|
keypath="admin_dash.frontend.is_default_custom"
|
||||||
|
v-else
|
||||||
|
>
|
||||||
|
<template #version>
|
||||||
|
<code>{{ adminDraft[':pleroma'][':frontends'][':primary'].ref }}</code>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</span>
|
||||||
|
<dl>
|
||||||
|
<dt>{{ $t('admin_dash.frontend.repository') }}</dt>
|
||||||
|
<dd><a :href="frontend.git" target="_blank">{{ frontend.git }}</a></dd>
|
||||||
|
<template v-if="expertLevel">
|
||||||
|
<dt>{{ $t('admin_dash.frontend.versions') }}</dt>
|
||||||
|
<dd v-for="ref in frontend.refs" :key="ref"><code>{{ ref }}</code></dd>
|
||||||
|
</template>
|
||||||
|
<dt v-if="expertLevel">{{ $t('admin_dash.frontend.build_url') }}</dt>
|
||||||
|
<dd v-if="expertLevel"><a :href="frontend.build_url" target="_blank">{{ frontend.build_url }}</a></dd>
|
||||||
|
</dl>
|
||||||
|
<div>
|
||||||
|
<span class="btn-group">
|
||||||
|
<button
|
||||||
|
class="button button-default btn"
|
||||||
|
type="button"
|
||||||
|
:title="$t('admin_dash.frontend.update')"
|
||||||
|
@click="update(frontend)"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
frontend.installed
|
||||||
|
? $t('admin_dash.frontend.reinstall')
|
||||||
|
: $t('admin_dash.frontend.install')
|
||||||
|
}}
|
||||||
|
</button>
|
||||||
|
<Popover
|
||||||
|
v-if="frontend.refs.length > 1"
|
||||||
|
trigger="click"
|
||||||
|
class="button-dropdown"
|
||||||
|
placement="bottom"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div class="dropdown-menu">
|
||||||
|
<button
|
||||||
|
v-for="ref in frontend.refs"
|
||||||
|
:key="ref"
|
||||||
|
class="button-default dropdown-item"
|
||||||
|
@click="update(frontend, ref)"
|
||||||
|
>
|
||||||
|
<i18n-t keypath="admin_dash.frontend.install_version">
|
||||||
|
<template #version>
|
||||||
|
<code>{{ ref }}</code>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #trigger>
|
||||||
|
<button
|
||||||
|
class="button button-default btn dropdown-button"
|
||||||
|
type="button"
|
||||||
|
:title="$t('admin_dash.frontend.update')"
|
||||||
|
>
|
||||||
|
<FAIcon icon="chevron-down" />
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
|
</span>
|
||||||
|
<span class="btn-group" v-if="frontend.name !== 'admin-fe'">
|
||||||
|
<button
|
||||||
|
class="button button-default btn"
|
||||||
|
type="button"
|
||||||
|
:disabled="
|
||||||
|
adminDraft[':pleroma'][':frontends'][':primary'].name === frontend.name &&
|
||||||
|
adminDraft[':pleroma'][':frontends'][':primary'].ref === frontend.refs[0]
|
||||||
|
"
|
||||||
|
:title="$t('admin_dash.frontend.update')"
|
||||||
|
@click="setDefault(frontend)"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
$t('admin_dash.frontend.set_default')
|
||||||
|
}}
|
||||||
|
</button>
|
||||||
|
{{ ' ' }}
|
||||||
|
<Popover
|
||||||
|
v-if="frontend.refs.length > 1"
|
||||||
|
trigger="click"
|
||||||
|
class="button-dropdown"
|
||||||
|
placement="bottom"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div class="dropdown-menu">
|
||||||
|
<button
|
||||||
|
v-for="ref in frontend.refs.slice(1)"
|
||||||
|
:key="ref"
|
||||||
|
class="button-default dropdown-item"
|
||||||
|
@click="setDefault(frontend, ref)"
|
||||||
|
>
|
||||||
|
<i18n-t keypath="admin_dash.frontend.set_default_version">
|
||||||
|
<template #version>
|
||||||
|
<code>{{ ref }}</code>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #trigger>
|
||||||
|
<button
|
||||||
|
class="button button-default btn dropdown-button"
|
||||||
|
type="button"
|
||||||
|
:title="$t('admin_dash.frontend.update')"
|
||||||
|
>
|
||||||
|
<FAIcon icon="chevron-down" />
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./frontends_tab.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss" src="./frontends_tab.scss"></style>
|
|
@ -7,7 +7,6 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
...Setting.computed,
|
...Setting.computed,
|
||||||
isDirty () {
|
isDirty () {
|
||||||
console.log(this.state, this.draft)
|
|
||||||
return !isEqual(this.state, this.draft)
|
return !isEqual(this.state, this.draft)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ export default {
|
||||||
// TODO allow passing shared draft object?
|
// TODO allow passing shared draft object?
|
||||||
get () {
|
get () {
|
||||||
if (this.realSource === 'admin') {
|
if (this.realSource === 'admin') {
|
||||||
return get(this.$store.state.adminSettings.draft, this.path)
|
return get(this.$store.state.adminSettings.draft, this.canonPath)
|
||||||
} else {
|
} else {
|
||||||
return this.localDraft
|
return this.localDraft
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
state () {
|
state () {
|
||||||
const value = get(this.configSource, this.path)
|
const value = get(this.configSource, this.canonPath)
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
return this.defaultState
|
return this.defaultState
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -10,6 +10,9 @@ const SharedComputedObject = () => ({
|
||||||
},
|
},
|
||||||
adminConfig () {
|
adminConfig () {
|
||||||
return this.$store.state.adminSettings.config
|
return this.$store.state.adminSettings.config
|
||||||
|
},
|
||||||
|
adminDraft () {
|
||||||
|
return this.$store.state.adminSettings.draft
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import getResettableAsyncComponent from 'src/services/resettable_async_component
|
||||||
import Popover from '../popover/popover.vue'
|
import Popover from '../popover/popover.vue'
|
||||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { cloneDeep } from 'lodash'
|
import { cloneDeep, isEqual } from 'lodash'
|
||||||
import {
|
import {
|
||||||
newImporter,
|
newImporter,
|
||||||
newExporter
|
newExporter
|
||||||
|
@ -155,6 +155,12 @@ const SettingsModal = {
|
||||||
PLEROMAFE_SETTINGS_MINOR_VERSION
|
PLEROMAFE_SETTINGS_MINOR_VERSION
|
||||||
]
|
]
|
||||||
return clone
|
return clone
|
||||||
|
},
|
||||||
|
resetAdminDraft () {
|
||||||
|
this.$store.commit('resetAdminDraft')
|
||||||
|
},
|
||||||
|
pushAdminDraft () {
|
||||||
|
this.$store.dispatch('pushAdminDraft')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -183,6 +189,12 @@ const SettingsModal = {
|
||||||
set (value) {
|
set (value) {
|
||||||
this.$store.dispatch('setOption', { name: 'expertLevel', value: value ? 1 : 0 })
|
this.$store.dispatch('setOption', { name: 'expertLevel', value: value ? 1 : 0 })
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
adminDraftAny () {
|
||||||
|
return !isEqual(
|
||||||
|
this.$store.state.adminSettings.config,
|
||||||
|
this.$store.state.adminSettings.draft
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,9 @@
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
min-height: 2em;
|
min-height: 2em;
|
||||||
min-width: 10em;
|
}
|
||||||
|
|
||||||
|
.btn:not(.dropdown-button) {
|
||||||
padding: 0 2em;
|
padding: 0 2em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,6 +53,8 @@
|
||||||
|
|
||||||
.settings-footer {
|
.settings-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
line-height: 2;
|
||||||
|
|
||||||
>* {
|
>* {
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
<SettingsModalUserContent v-if="modalMode === 'user' && modalOpenedOnceUser" />
|
<SettingsModalUserContent v-if="modalMode === 'user' && modalOpenedOnceUser" />
|
||||||
<SettingsModalAdminContent v-if="modalMode === 'admin' && modalOpenedOnceAdmin" />
|
<SettingsModalAdminContent v-if="modalMode === 'admin' && modalOpenedOnceAdmin" />
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-footer settings-footer">
|
<div class="panel-footer settings-footer -flexible-height">
|
||||||
<Popover
|
<Popover
|
||||||
v-if="modalMode === 'user'"
|
v-if="modalMode === 'user'"
|
||||||
class="export"
|
class="export"
|
||||||
|
@ -125,6 +125,23 @@
|
||||||
id="unscrolled-content"
|
id="unscrolled-content"
|
||||||
class="extra-content"
|
class="extra-content"
|
||||||
/>
|
/>
|
||||||
|
<span class="admin-buttons" v-if="modalMode === 'admin'">
|
||||||
|
<button
|
||||||
|
class="button-default btn"
|
||||||
|
@click="resetAdminDraft"
|
||||||
|
:disabled="!adminDraftAny"
|
||||||
|
>
|
||||||
|
{{ $t("admin_dash.reset_all") }}
|
||||||
|
</button>
|
||||||
|
{{ ' ' }}
|
||||||
|
<button
|
||||||
|
class="button-default btn"
|
||||||
|
@click="pushAdminDraft"
|
||||||
|
:disabled="!adminDraftAny"
|
||||||
|
>
|
||||||
|
{{ $t("admin_dash.commit_all") }}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
||||||
|
|
||||||
import DataImportExportTab from './tabs/data_import_export_tab.vue'
|
|
||||||
import MutesAndBlocksTab from './tabs/mutes_and_blocks_tab.vue'
|
|
||||||
import InstanceTab from './admin_tabs/instance_tab.vue'
|
import InstanceTab from './admin_tabs/instance_tab.vue'
|
||||||
import LimitsTab from './admin_tabs/limits_tab.vue'
|
import LimitsTab from './admin_tabs/limits_tab.vue'
|
||||||
|
import FrontendsTab from './admin_tabs/frontends_tab.vue'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
faWrench,
|
faWrench,
|
||||||
faHand,
|
faHand,
|
||||||
faFilter,
|
faLaptopCode,
|
||||||
faPaintBrush,
|
faPaintBrush,
|
||||||
faBell,
|
faBell,
|
||||||
faDownload,
|
faDownload,
|
||||||
|
@ -20,7 +19,7 @@ import {
|
||||||
library.add(
|
library.add(
|
||||||
faWrench,
|
faWrench,
|
||||||
faHand,
|
faHand,
|
||||||
faFilter,
|
faLaptopCode,
|
||||||
faPaintBrush,
|
faPaintBrush,
|
||||||
faBell,
|
faBell,
|
||||||
faDownload,
|
faDownload,
|
||||||
|
@ -32,12 +31,14 @@ const SettingsModalAdminContent = {
|
||||||
components: {
|
components: {
|
||||||
TabSwitcher,
|
TabSwitcher,
|
||||||
|
|
||||||
DataImportExportTab,
|
|
||||||
MutesAndBlocksTab,
|
|
||||||
InstanceTab,
|
InstanceTab,
|
||||||
LimitsTab
|
LimitsTab,
|
||||||
|
FrontendsTab
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
user () {
|
||||||
|
return this.$store.state.users.currentUser
|
||||||
|
},
|
||||||
isLoggedIn () {
|
isLoggedIn () {
|
||||||
return !!this.$store.state.users.currentUser
|
return !!this.$store.state.users.currentUser
|
||||||
},
|
},
|
||||||
|
@ -46,6 +47,20 @@ const SettingsModalAdminContent = {
|
||||||
},
|
},
|
||||||
bodyLock () {
|
bodyLock () {
|
||||||
return this.$store.state.interface.settingsModalState === 'visible'
|
return this.$store.state.interface.settingsModalState === 'visible'
|
||||||
|
},
|
||||||
|
adminDbLoaded () {
|
||||||
|
return this.$store.state.adminSettings.loaded
|
||||||
|
},
|
||||||
|
adminDescriptionsLoaded () {
|
||||||
|
return this.$store.state.adminSettings.descriptions !== null
|
||||||
|
},
|
||||||
|
noDb () {
|
||||||
|
return this.$store.state.adminSettings.dbConfigEnabled === false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
if (this.user.rights.admin) {
|
||||||
|
this.$store.dispatch('loadAdminStuff')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -1,12 +1,44 @@
|
||||||
<template>
|
<template>
|
||||||
<tab-switcher
|
<tab-switcher
|
||||||
|
v-if="adminDescriptionsLoaded && (noDb || adminDbLoaded)"
|
||||||
ref="tabSwitcher"
|
ref="tabSwitcher"
|
||||||
class="settings_tab-switcher"
|
class="settings_tab-switcher"
|
||||||
:side-tab-bar="true"
|
:side-tab-bar="true"
|
||||||
:scrollable-tabs="true"
|
:scrollable-tabs="true"
|
||||||
|
:render-only-focused="true"
|
||||||
:body-scroll-lock="bodyLock"
|
:body-scroll-lock="bodyLock"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
v-if="noDb"
|
||||||
|
:label="$t('admin_dash.tabs.nodb')"
|
||||||
|
icon="exclamation-triangle"
|
||||||
|
data-tab-name="nodb-notice"
|
||||||
|
>
|
||||||
|
<div :label="$t('admin_dash.tabs.nodb')">
|
||||||
|
<div class="setting-item">
|
||||||
|
<h2>{{ $t('admin_dash.nodb.heading') }}</h2>
|
||||||
|
<i18n-t keypath="admin_dash.nodb.text">
|
||||||
|
<template #documentation>
|
||||||
|
<a
|
||||||
|
href="https://docs-develop.pleroma.social/backend/configuration/howto_database_config/"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{{ $t("admin_dash.nodb.documentation") }}
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
<template #property>
|
||||||
|
<code>config :pleroma, configurable_from_database</code>
|
||||||
|
</template>
|
||||||
|
<template #value>
|
||||||
|
<code>true</code>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
<p>{{ $t('admin_dash.nodb.text2') }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="adminDbLoaded"
|
||||||
:label="$t('admin_dash.tabs.instance')"
|
:label="$t('admin_dash.tabs.instance')"
|
||||||
icon="wrench"
|
icon="wrench"
|
||||||
data-tab-name="general"
|
data-tab-name="general"
|
||||||
|
@ -14,12 +46,20 @@
|
||||||
<InstanceTab />
|
<InstanceTab />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
v-if="adminDbLoaded"
|
||||||
:label="$t('admin_dash.tabs.limits')"
|
:label="$t('admin_dash.tabs.limits')"
|
||||||
icon="hand"
|
icon="hand"
|
||||||
data-tab-name="limits"
|
data-tab-name="limits"
|
||||||
>
|
>
|
||||||
<LimitsTab />
|
<LimitsTab />
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
:label="$t('admin_dash.tabs.frontends')"
|
||||||
|
icon="laptop-code"
|
||||||
|
data-tab-name="frontends"
|
||||||
|
>
|
||||||
|
<FrontendsTab />
|
||||||
|
</div>
|
||||||
</tab-switcher>
|
</tab-switcher>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -842,9 +842,19 @@
|
||||||
"window_title": "Administration",
|
"window_title": "Administration",
|
||||||
"wip_notice": "This admin dashboard is experimental and WIP, {adminFeLink}.",
|
"wip_notice": "This admin dashboard is experimental and WIP, {adminFeLink}.",
|
||||||
"old_ui_link": "old admin UI available here",
|
"old_ui_link": "old admin UI available here",
|
||||||
|
"reset_all": "Reset all",
|
||||||
|
"commit_all": "Save all",
|
||||||
"tabs": {
|
"tabs": {
|
||||||
|
"nodb": "No DB Config",
|
||||||
"instance": "Instance",
|
"instance": "Instance",
|
||||||
"limits": "Limits"
|
"limits": "Limits",
|
||||||
|
"frontends": "Front-ends"
|
||||||
|
},
|
||||||
|
"nodb": {
|
||||||
|
"heading": "Database config is disabled",
|
||||||
|
"text": "You need to change backend config files so that {property} is set to {value}, see more in {documentation}.",
|
||||||
|
"documentation": "documentation",
|
||||||
|
"text2": "Most configuration options will be unavailable."
|
||||||
},
|
},
|
||||||
"captcha": {
|
"captcha": {
|
||||||
"native": "Native",
|
"native": "Native",
|
||||||
|
@ -863,6 +873,23 @@
|
||||||
"users": "User profile limits",
|
"users": "User profile limits",
|
||||||
"profile_fields": "Profile fields limits",
|
"profile_fields": "Profile fields limits",
|
||||||
"user_uploads": "Profile media limits"
|
"user_uploads": "Profile media limits"
|
||||||
|
},
|
||||||
|
"frontend": {
|
||||||
|
"repository": "Repository link",
|
||||||
|
"versions": "Available versions",
|
||||||
|
"build_url": "Build URL",
|
||||||
|
"reinstall": "Reinstall",
|
||||||
|
"is_default": "(Default)",
|
||||||
|
"is_default_custom": "(Default, version: {version})",
|
||||||
|
"install": "Install",
|
||||||
|
"install_version": "Install version {version}",
|
||||||
|
"set_default": "Set default",
|
||||||
|
"set_default_version": "Set version {version} as default",
|
||||||
|
"wip_notice": "Please note that this section is a WIP and lacks certain features as backend implementation of front-end management is incomplete.",
|
||||||
|
"default_frontend": "Default front-end",
|
||||||
|
"default_frontend_tip": "Default front-end will be shown to all users. Currently there's no way to for a user to select personal front-end. If you switch away from PleromaFE you'll most likely have to use old and buggy AdminFE to do instance configuration until we replace it.",
|
||||||
|
"default_frontend_tip2": "WIP: Since Pleroma backend doesn't properly list all installed frontends you'll have to enter name and reference manually. List below provides shortcuts to fill the values.",
|
||||||
|
"available_frontends": "Available for install"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"time": {
|
"time": {
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import { set, get, cloneDeep } from 'lodash'
|
import { set, get, cloneDeep, differenceWith, isEqual, flatten } from 'lodash'
|
||||||
|
|
||||||
export const defaultState = {
|
export const defaultState = {
|
||||||
|
frontends: [],
|
||||||
|
loaded: false,
|
||||||
needsReboot: null,
|
needsReboot: null,
|
||||||
config: null,
|
config: null,
|
||||||
modifiedPaths: null,
|
modifiedPaths: null,
|
||||||
descriptions: null,
|
descriptions: null,
|
||||||
draft: null
|
draft: null,
|
||||||
|
dbConfigEnabled: null
|
||||||
}
|
}
|
||||||
|
|
||||||
export const newUserFlags = {
|
export const newUserFlags = {
|
||||||
|
@ -17,7 +20,23 @@ const adminSettingsStorage = {
|
||||||
...cloneDeep(defaultState)
|
...cloneDeep(defaultState)
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
|
setInstanceAdminNoDbConfig (state) {
|
||||||
|
state.loaded = false
|
||||||
|
state.dbConfigEnabled = false
|
||||||
|
},
|
||||||
|
setAvailableFrontends (state, { frontends }) {
|
||||||
|
state.frontends = frontends.map(f => {
|
||||||
|
if (f.name === 'pleroma-fe') {
|
||||||
|
f.refs = ['master', 'develop']
|
||||||
|
} else {
|
||||||
|
f.refs = [f.ref]
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
})
|
||||||
|
},
|
||||||
updateAdminSettings (state, { config, modifiedPaths }) {
|
updateAdminSettings (state, { config, modifiedPaths }) {
|
||||||
|
state.loaded = true
|
||||||
|
state.dbConfigEnabled = true
|
||||||
state.config = config
|
state.config = config
|
||||||
state.modifiedPaths = modifiedPaths
|
state.modifiedPaths = modifiedPaths
|
||||||
},
|
},
|
||||||
|
@ -40,9 +59,33 @@ const adminSettingsStorage = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
loadFrontendsStuff ({ state, rootState, dispatch, commit }) {
|
||||||
|
rootState.api.backendInteractor.fetchAvailableFrontends()
|
||||||
|
.then(frontends => commit('setAvailableFrontends', { frontends }))
|
||||||
|
},
|
||||||
|
loadAdminStuff ({ state, rootState, dispatch, commit }) {
|
||||||
|
rootState.api.backendInteractor.fetchInstanceDBConfig()
|
||||||
|
.then(backendDbConfig => {
|
||||||
|
if (backendDbConfig.error) {
|
||||||
|
if (backendDbConfig.error.status === 400) {
|
||||||
|
backendDbConfig.error.json().then(errorJson => {
|
||||||
|
if (/configurable_from_database/.test(errorJson.error)) {
|
||||||
|
commit('setInstanceAdminNoDbConfig')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dispatch('setInstanceAdminSettings', { backendDbConfig })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (state.descriptions === null) {
|
||||||
|
rootState.api.backendInteractor.fetchInstanceConfigDescriptions()
|
||||||
|
.then(backendDescriptions => dispatch('setInstanceAdminDescriptions', { backendDescriptions }))
|
||||||
|
}
|
||||||
|
},
|
||||||
setInstanceAdminSettings ({ state, commit, dispatch }, { backendDbConfig }) {
|
setInstanceAdminSettings ({ state, commit, dispatch }, { backendDbConfig }) {
|
||||||
const config = state.config || {}
|
const config = state.config || {}
|
||||||
const modifiedPaths = state.modifiedPaths || new Set()
|
const modifiedPaths = new Set()
|
||||||
backendDbConfig.configs.forEach(c => {
|
backendDbConfig.configs.forEach(c => {
|
||||||
const path = [c.group, c.key]
|
const path = [c.group, c.key]
|
||||||
if (c.db) {
|
if (c.db) {
|
||||||
|
@ -82,6 +125,59 @@ const adminSettingsStorage = {
|
||||||
console.log(descriptions[':pleroma']['Pleroma.Captcha'])
|
console.log(descriptions[':pleroma']['Pleroma.Captcha'])
|
||||||
commit('updateAdminDescriptions', { descriptions })
|
commit('updateAdminDescriptions', { descriptions })
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// This action takes draft state, diffs it with live config state and then pushes
|
||||||
|
// only differences between the two. Difference detection only work up to subkey (third) level.
|
||||||
|
pushAdminDraft ({ rootState, state, commit, dispatch }) {
|
||||||
|
// TODO cleanup paths in modifiedPaths
|
||||||
|
const convert = (value) => {
|
||||||
|
if (typeof value !== 'object') {
|
||||||
|
return value
|
||||||
|
} else if (Array.isArray(value)) {
|
||||||
|
return value.map(convert)
|
||||||
|
} else {
|
||||||
|
return Object.entries(value).map(([k, v]) => ({ tuple: [k, v] }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getting all group-keys used in config
|
||||||
|
const allGroupKeys = flatten(
|
||||||
|
Object
|
||||||
|
.entries(state.config)
|
||||||
|
.map(
|
||||||
|
([group, lv1data]) => Object
|
||||||
|
.keys(lv1data)
|
||||||
|
.map((key) => ({ group, key }))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Only using group-keys where there are changes detected
|
||||||
|
const changedGroupKeys = allGroupKeys.filter(({ group, key }) => {
|
||||||
|
return !isEqual(state.config[group][key], state.draft[group][key])
|
||||||
|
})
|
||||||
|
|
||||||
|
// Here we take all changed group-keys and get all changed subkeys
|
||||||
|
const changed = changedGroupKeys.map(({ group, key }) => {
|
||||||
|
const config = state.config[group][key]
|
||||||
|
const draft = state.draft[group][key]
|
||||||
|
|
||||||
|
// We convert group-key value into entries arrays
|
||||||
|
const eConfig = Object.entries(config)
|
||||||
|
const eDraft = Object.entries(draft)
|
||||||
|
|
||||||
|
// Then those entries array we diff so only changed subkey entries remain
|
||||||
|
// We use the diffed array to reconstruct the object and then shove it into convert()
|
||||||
|
return ({ group, key, value: convert(Object.fromEntries(differenceWith(eDraft, eConfig, isEqual))) })
|
||||||
|
})
|
||||||
|
|
||||||
|
rootState.api.backendInteractor.pushInstanceDBConfig({
|
||||||
|
payload: {
|
||||||
|
configs: changed
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() => rootState.api.backendInteractor.fetchInstanceDBConfig())
|
||||||
|
.then(backendDbConfig => dispatch('setInstanceAdminSettings', { backendDbConfig }))
|
||||||
|
},
|
||||||
pushAdminSetting ({ rootState, state, commit, dispatch }, { path, value }) {
|
pushAdminSetting ({ rootState, state, commit, dispatch }, { path, value }) {
|
||||||
const [group, key, ...rest] = Array.isArray(path) ? path : path.split(/\./g)
|
const [group, key, ...rest] = Array.isArray(path) ? path : path.split(/\./g)
|
||||||
const clone = {} // not actually cloning the entire thing to avoid excessive writes
|
const clone = {} // not actually cloning the entire thing to avoid excessive writes
|
||||||
|
|
|
@ -564,12 +564,6 @@ const users = {
|
||||||
user.domainMutes = []
|
user.domainMutes = []
|
||||||
commit('setCurrentUser', user)
|
commit('setCurrentUser', user)
|
||||||
commit('setServerSideStorage', user)
|
commit('setServerSideStorage', user)
|
||||||
if (user.rights.admin) {
|
|
||||||
store.rootState.api.backendInteractor.fetchInstanceDBConfig()
|
|
||||||
.then(backendDbConfig => dispatch('setInstanceAdminSettings', { backendDbConfig }))
|
|
||||||
store.rootState.api.backendInteractor.fetchInstanceConfigDescriptions()
|
|
||||||
.then(backendDescriptions => dispatch('setInstanceAdminDescriptions', { backendDescriptions }))
|
|
||||||
}
|
|
||||||
commit('addNewUsers', [user])
|
commit('addNewUsers', [user])
|
||||||
|
|
||||||
dispatch('fetchEmoji')
|
dispatch('fetchEmoji')
|
||||||
|
|
|
@ -110,6 +110,8 @@ const PLEROMA_DELETE_ANNOUNCEMENT_URL = id => `/api/v1/pleroma/admin/announcemen
|
||||||
|
|
||||||
const PLEROMA_ADMIN_CONFIG_URL = '/api/pleroma/admin/config'
|
const PLEROMA_ADMIN_CONFIG_URL = '/api/pleroma/admin/config'
|
||||||
const PLEROMA_ADMIN_DESCRIPTIONS_URL = '/api/pleroma/admin/config/descriptions'
|
const PLEROMA_ADMIN_DESCRIPTIONS_URL = '/api/pleroma/admin/config/descriptions'
|
||||||
|
const PLEROMA_ADMIN_FRONTENDS_URL = '/api/pleroma/admin/frontends'
|
||||||
|
const PLEROMA_ADMIN_FRONTENDS_INSTALL_URL = '/api/pleroma/admin/frontends/install'
|
||||||
|
|
||||||
const oldfetch = window.fetch
|
const oldfetch = window.fetch
|
||||||
|
|
||||||
|
@ -1693,6 +1695,21 @@ const fetchInstanceConfigDescriptions = ({ credentials }) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fetchAvailableFrontends = ({ credentials }) => {
|
||||||
|
return fetch(PLEROMA_ADMIN_FRONTENDS_URL, {
|
||||||
|
headers: authHeaders(credentials)
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.json()
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
error: response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const pushInstanceDBConfig = ({ credentials, payload }) => {
|
const pushInstanceDBConfig = ({ credentials, payload }) => {
|
||||||
return fetch(PLEROMA_ADMIN_CONFIG_URL, {
|
return fetch(PLEROMA_ADMIN_CONFIG_URL, {
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -1714,6 +1731,27 @@ const pushInstanceDBConfig = ({ credentials, payload }) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const installFrontend = ({ credentials, payload }) => {
|
||||||
|
return fetch(PLEROMA_ADMIN_FRONTENDS_INSTALL_URL, {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
...authHeaders(credentials)
|
||||||
|
},
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(payload)
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.json()
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
error: response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const apiService = {
|
const apiService = {
|
||||||
verifyCredentials,
|
verifyCredentials,
|
||||||
fetchTimeline,
|
fetchTimeline,
|
||||||
|
@ -1830,7 +1868,9 @@ const apiService = {
|
||||||
adminFetchAnnouncements,
|
adminFetchAnnouncements,
|
||||||
fetchInstanceDBConfig,
|
fetchInstanceDBConfig,
|
||||||
fetchInstanceConfigDescriptions,
|
fetchInstanceConfigDescriptions,
|
||||||
pushInstanceDBConfig
|
fetchAvailableFrontends,
|
||||||
|
pushInstanceDBConfig,
|
||||||
|
installFrontend
|
||||||
}
|
}
|
||||||
|
|
||||||
export default apiService
|
export default apiService
|
||||||
|
|
Loading…
Add table
Reference in a new issue