diff --git a/client/components/admin/admin-theme.vue b/client/components/admin/admin-theme.vue index 6443f1b6588231c31341159e6d1e38b98c26b89c..b3aed1a139e619aa4f2173d33cf8a633d9d49e24 100644 --- a/client/components/admin/admin-theme.vue +++ b/client/components/admin/admin-theme.vue @@ -1,5 +1,5 @@ <template lang='pug'> -v-container(fluid, grid-list-lg) + v-container(fluid, grid-list-lg) v-layout(row wrap) v-flex(xs12) .admin-header @@ -54,54 +54,17 @@ v-container(fluid, grid-list-lg) v-card.mt-3.animated.fadeInUp.wait-p1s v-toolbar(color='primary', dark, dense, flat) v-toolbar-title.subtitle-1 {{$t(`admin:theme.options`)}} - v-spacer - v-chip(label, color='white', small).primary--text coming soon v-card-text - v-select( - :items='[]' - outlined - prepend-icon='mdi-border-vertical' - v-model='config.iconset' - label='Table of Contents Position' - persistent-hint - hint='Select whether the table of contents is shown on the left, right or not at all.' - disabled - ) v-range-slider( - prepend-icon='mdi-serial-port' - label='Heading Levels in ToC' - hint='The table of contents will show headings from and up to the selected levels.' + prepend-icon='mdi-menu-open' + :label='$t(`admin:theme.tocHeadingLevels`)' v-model='tocRange' :min='1' :max='6' :tick-labels='["H1", "H2", "H3", "H4", "H5", "H6"]' ) + .text-caption {{$t('admin:theme.tocHeadingLevelsHint')}} v-flex(lg6 xs12) - //- v-card.animated.fadeInUp.wait-p2s - //- v-toolbar(color='teal', dark, dense, flat) - //- v-toolbar-title.subtitle-1 {{$t('admin:theme.downloadThemes')}} - //- v-spacer - //- v-chip(label, color='white', small).teal--text coming soon - //- v-data-table( - //- :headers='headers', - //- :items='themes', - //- hide-default-footer, - //- item-key='value', - //- :items-per-page='1000' - //- ) - //- template(v-slot:item='thm') - //- td - //- strong {{thm.item.text}} - //- td - //- span {{ thm.item.author }} - //- td.text-xs-center - //- v-progress-circular(v-if='thm.item.isDownloading', indeterminate, color='blue', size='20', :width='2') - //- v-btn(v-else-if='thm.item.isInstalled && thm.item.installDate < thm.item.updatedAt', icon) - //- v-icon.blue--text mdi-cached - //- v-btn(v-else-if='thm.item.isInstalled', icon) - //- v-icon.green--text mdi-check-bold - //- v-btn(v-else, icon) - //- v-icon.grey--text mdi-cloud-download v-card.animated.fadeInUp.wait-p2s v-toolbar(color='primary', dark, dense, flat) @@ -161,9 +124,10 @@ export default { config: { theme: 'default', darkMode: false, - minTocLevel: 0, - tocLevel: 2, - tocCollapseLevel: 2, + tocDepth: { + min: 1, + max: 2 + }, iconset: '', injectCSS: '', injectHead: '', @@ -175,13 +139,14 @@ export default { computed: { tocRange: { get() { - var range = [this.config.minTocLevel, this.config.tocLevel] + var range = [this.config.tocDepth.min, this.config.tocDepth.max] return range }, set(value) { - this.config.minTocLevel = value[0] - this.config.tocLevel = value[1] - this.config.tocCollapseLevel = value[1] + this.config.tocDepth = { + min: parseInt(value[0]), + max: parseInt(value[1]) + } } }, darkMode: sync('site/dark'), @@ -230,9 +195,7 @@ export default { theme: this.config.theme, iconset: this.config.iconset, darkMode: this.darkMode, - minTocLevel: parseInt(this.config.minTocLevel, 10), - tocLevel: parseInt(this.config.tocLevel, 10), - tocCollapseLevel: parseInt(this.config.tocCollapseLevel, 10), + tocDepth: this.config.tocDepth, injectCSS: this.config.injectCSS, injectHead: this.config.injectHead, injectBody: this.config.injectBody diff --git a/client/components/editor.vue b/client/components/editor.vue index 0a589897e2c86c9b390feb4365a5f16e2372726e..530c99fbffa888f59c3b6b663086ea052d911088 100644 --- a/client/components/editor.vue +++ b/client/components/editor.vue @@ -144,21 +144,9 @@ export default { type: Number, default: 0 }, - minTocLevel: { - type: Number, - default: 0 - }, - tocLevel: { - type: Number, - default: 1 - }, - tocCollapseLevel: { - type: Number, - default: 0 - }, - doUseTocDefault: { - type: Boolean, - default: true + tocOptions: { + type: String, + default: '' }, checkoutDate: { type: String, @@ -187,7 +175,9 @@ export default { tags: '', title: '', css: '', - js: '' + js: '', + tocDepth: 0, + useDefaultTocDepth: false } } }, @@ -206,10 +196,8 @@ export default { this.path !== this.$store.get('page/path'), this.savedState.title !== this.$store.get('page/title'), this.savedState.description !== this.$store.get('page/description'), - this.savedState.minTocLevel !== this.$store.get('page/minTocLevel'), - this.savedState.tocLevel !== this.$store.get('page/tocLevel'), - this.savedState.tocCollapseLevel !== this.$store.get('page/tocCollapseLevel'), - this.savedState.doUseTocDefault !== this.$store.get('page/doUseTocDefault'), + this.savedState.tocDepth !== this.$store.get('page/tocDepth@min') + (this.$store.get('page/tocDepth@max') * 10), + this.savedState.useDefaultTocDepth !== this.$store.get('page/useDefaultTocDepth'), this.savedState.tags !== this.$store.get('page/tags'), this.savedState.isPublished !== this.$store.get('page/isPublished'), this.savedState.publishStartDate !== this.$store.get('page/publishStartDate'), @@ -243,12 +231,15 @@ export default { this.$store.set('page/title', this.title) this.$store.set('page/scriptCss', this.scriptCss) this.$store.set('page/scriptJs', this.scriptJs) - this.$store.set('page/minTocLevel', this.minTocLevel) - this.$store.set('page/tocLevel', this.tocLevel) - this.$store.set('page/tocCollapseLevel', this.tocCollapseLevel) - this.$store.set('page/doUseTocDefault', this.doUseTocDefault) this.$store.set('page/mode', 'edit') + const tocOptions = JSON.parse(Buffer.from(this.tocOptions, 'base64').toString()) + this.$store.set('page/tocDepth', { + min: tocOptions.min, + max: tocOptions.max + }) + this.$store.set('page/useDefaultTocDepth', tocOptions.useDefault) + this.setCurrentSavedState() this.checkoutDateActive = this.checkoutDate @@ -326,10 +317,8 @@ export default { $publishStartDate: Date $scriptCss: String $scriptJs: String - $minTocLevel: Int! - $tocLevel: Int! - $tocCollapseLevel: Int! - $doUseTocDefault: Boolean! + $tocDepth: RangeInput + $useDefaultTocDepth: Boolean $tags: [String]! $title: String! ) { @@ -346,10 +335,8 @@ export default { publishStartDate: $publishStartDate scriptCss: $scriptCss scriptJs: $scriptJs - minTocLevel: $minTocLevel - tocLevel: $tocLevel - tocCollapseLevel: $tocCollapseLevel - doUseTocDefault: $doUseTocDefault + tocDepth: $tocDepth + useDefaultTocDepth: $useDefaultTocDepth tags: $tags title: $title ) { @@ -379,10 +366,8 @@ export default { publishStartDate: this.$store.get('page/publishStartDate') || '', scriptCss: this.$store.get('page/scriptCss'), scriptJs: this.$store.get('page/scriptJs'), - minTocLevel: this.$store.get('page/minTocLevel'), - tocLevel: this.$store.get('page/tocLevel'), - tocCollapseLevel: this.$store.get('page/tocCollapseLevel'), - doUseTocDefault: this.$store.get('page/doUseTocDefault'), + tocDepth: this.$store.get('page/tocDepth'), + useDefaultTocDepth: this.$store.get('page/useDefaultTocDepth'), tags: this.$store.get('page/tags'), title: this.$store.get('page/title') } @@ -441,10 +426,8 @@ export default { $publishStartDate: Date $scriptCss: String $scriptJs: String - $minTocLevel: Int - $tocLevel: Int - $tocCollapseLevel: Int - $doUseTocDefault: Boolean + $tocDepth: RangeInput + $useDefaultTocDepth: Boolean $tags: [String] $title: String ) { @@ -462,10 +445,8 @@ export default { publishStartDate: $publishStartDate scriptCss: $scriptCss scriptJs: $scriptJs - minTocLevel: $minTocLevel - tocLevel: $tocLevel - tocCollapseLevel: $tocCollapseLevel - doUseTocDefault: $doUseTocDefault + tocDepth: $tocDepth + useDefaultTocDepth: $useDefaultTocDepth tags: $tags title: $title ) { @@ -495,10 +476,8 @@ export default { publishStartDate: this.$store.get('page/publishStartDate') || '', scriptCss: this.$store.get('page/scriptCss'), scriptJs: this.$store.get('page/scriptJs'), - minTocLevel: this.$store.get('page/minTocLevel'), - tocLevel: this.$store.get('page/tocLevel'), - tocCollapseLevel: this.$store.get('page/tocCollapseLevel'), - doUseTocDefault: this.$store.get('page/doUseTocDefault'), + tocDepth: this.$store.get('page/tocDepth'), + useDefaultTocDepth: this.$store.get('page/useDefaultTocDepth'), tags: this.$store.get('page/tags'), title: this.$store.get('page/title') } @@ -582,10 +561,8 @@ export default { title: this.$store.get('page/title'), css: this.$store.get('page/scriptCss'), js: this.$store.get('page/scriptJs'), - minTocLevel: this.$store.get('page/minTocLevel'), - tocLevel: this.$store.get('page/tocLevel'), - tocCollapseLevel: this.$store.get('page/tocCollapseLevel'), - doUseTocDefault: this.$store.get('page/doUseTocDefault') + tocDepth: this.$store.get('page/tocDepth@min') + (this.$store.get('page/tocDepth@max') * 10), + useDefaultTocDepth: this.$store.get('page/useDefaultTocDepth') } }, injectCustomCss: _.debounce(css => { diff --git a/client/components/editor/editor-modal-properties.vue b/client/components/editor/editor-modal-properties.vue index a0e8b22b94076928e6c1f327b4276d93db320c59..79a32e6cdbaff90aca712754c74e55f2a7bd7d4d 100644 --- a/client/components/editor/editor-modal-properties.vue +++ b/client/components/editor/editor-modal-properties.vue @@ -19,9 +19,9 @@ v-dialog( v-card(tile) v-tabs(color='white', background-color='blue darken-1', dark, centered, v-model='currentTab') v-tab {{$t('editor:props.info')}} + v-tab {{$t('editor:props.toc')}} v-tab {{$t('editor:props.scheduling')}} v-tab(:disabled='!hasScriptPermission') {{$t('editor:props.scripts')}} - v-tab(disabled) {{$t('editor:props.social')}} v-tab(:disabled='!hasStylePermission') {{$t('editor:props.styles')}} v-tab-item(transition='fade-transition', reverse-transition='fade-transition') v-card-text.pt-5 @@ -67,23 +67,6 @@ v-dialog( :rules='[rules.required, rules.path]' ) v-divider - v-card-text.grey.pt-5(:class='$vuetify.theme.dark ? `darken-3-d3` : `lighten-5`') - .overline.pb-5 Theme Options - v-switch( - label='Use Site Defaults' - v-model='doUseTocDefault' - ) - v-range-slider( - :disabled='doUseTocDefault' - prepend-icon='mdi-serial-port' - label='Heading Levels in ToC' - hint='The table of contents will show headings from and up to the selected levels.' - v-model='tocRange' - :min='1' - :max='6' - :tick-labels='["H1", "H2", "H3", "H4", "H5", "H6"]' - ) - v-divider v-card-text.grey.pt-5(:class='$vuetify.theme.dark ? `darken-3-d5` : `lighten-4`') .overline.pb-5 {{$t('editor:props.categorization')}} v-chip-group.radius-5.mb-5(column, v-if='tags && tags.length > 0') @@ -107,6 +90,24 @@ v-dialog( hide-no-data :search-input.sync='newTagSearch' ) + v-tab-item(transition='fade-transition', reverse-transition='fade-transition') + v-card-text + .overline {{$t('editor:props.tocTitle')}} + v-switch( + :label='$t(`editor:props.tocUseDefault`)' + v-model='useDefaultTocDepth' + ) + v-range-slider( + :disabled='useDefaultTocDepth' + prepend-icon='mdi-menu-open' + :label='$t(`editor:props.tocHeadingLevels`)' + v-model='tocDepth' + :min='1' + :max='6' + :tick-labels='["H1", "H2", "H3", "H4", "H5", "H6"]' + ) + .text-caption.pl-8.grey--text {{$t('editor:props.tocHeadingLevelsHint')}} + v-tab-item(transition='fade-transition', reverse-transition='fade-transition') v-card-text .overline {{$t('editor:props.publishState')}} @@ -213,43 +214,6 @@ v-dialog( .editor-props-codeeditor-hint .caption {{$t('editor:props.htmlHint')}} - v-tab-item(transition='fade-transition', reverse-transition='fade-transition') - v-card-text - .overline {{$t('editor:props.socialFeatures')}} - v-switch( - :label='$t(`editor:props.allowComments`)' - v-model='isPublished' - color='primary' - :hint='$t(`editor:props.allowCommentsHint`)' - persistent-hint - inset - ) - v-switch( - :label='$t(`editor:props.allowRatings`)' - v-model='isPublished' - color='primary' - :hint='$t(`editor:props.allowRatingsHint`)' - persistent-hint - disabled - inset - ) - v-switch( - :label='$t(`editor:props.displayAuthor`)' - v-model='isPublished' - color='primary' - :hint='$t(`editor:props.displayAuthorHint`)' - persistent-hint - inset - ) - v-switch( - :label='$t(`editor:props.displaySharingBar`)' - v-model='isPublished' - color='primary' - :hint='$t(`editor:props.displaySharingBarHint`)' - persistent-hint - inset - ) - v-tab-item(:transition='false', :reverse-transition='false') .editor-props-codeeditor-title .overline {{$t('editor:props.css')}} @@ -315,19 +279,19 @@ export default { isPublished: sync('page/isPublished'), publishStartDate: sync('page/publishStartDate'), publishEndDate: sync('page/publishEndDate'), - tocRange: { + tocDepth: { get() { - var range = [this.$store.get('page/minTocLevel'), this.$store.get('page/tocLevel')] - return range - // return [get('page/minTocLevel'), get('page/tocLevel')] + const tocDepth = this.$store.get('page/tocDepth') + return [tocDepth.min, tocDepth.max] }, set(value) { - this.$store.set('page/minTocLevel', value[0]) - this.$store.set('page/tocLevel', value[1]) - this.$store.set('page/tocCollapseLevel', value[1]) + this.$store.set('page/tocDepth', { + min: parseInt(value[0]), + max: parseInt(value[1]) + }) } }, - doUseTocDefault: sync('page/doUseTocDefault'), + useDefaultTocDepth: sync('page/useDefaultTocDepth'), scriptJs: sync('page/scriptJs'), scriptCss: sync('page/scriptCss'), hasScriptPermission: get('page/effectivePermissions@pages.script'), @@ -359,7 +323,7 @@ export default { if (this.cm) { this.cm.toTextArea() } - if (newValue === 2) { + if (newValue === 3) { this.$nextTick(() => { setTimeout(() => { this.loadEditor(this.$refs.codejs, 'html') diff --git a/client/graph/admin/theme/theme-mutation-save.gql b/client/graph/admin/theme/theme-mutation-save.gql index ab0425acd3b014e18effc87512de16ce853358af..ba4e43476bfdfc0fab8b5705f4db9008610e2a55 100644 --- a/client/graph/admin/theme/theme-mutation-save.gql +++ b/client/graph/admin/theme/theme-mutation-save.gql @@ -1,6 +1,22 @@ -mutation($theme: String!, $iconset: String!, $darkMode: Boolean!, $minTocLevel: Int!, $tocLevel: Int!, $tocCollapseLevel: Int!, $injectCSS: String, $injectHead: String, $injectBody: String) { +mutation( + $theme: String! + $iconset: String! + $darkMode: Boolean! + $tocDepth: RangeInput! + $injectCSS: String + $injectHead: String + $injectBody: String + ) { theming { - setConfig(theme: $theme, iconset: $iconset, darkMode: $darkMode, minTocLevel: $minTocLevel, tocLevel: $tocLevel, tocCollapseLevel: $tocCollapseLevel, injectCSS: $injectCSS, injectHead: $injectHead, injectBody: $injectBody) { + setConfig( + theme: $theme + iconset: $iconset + darkMode: $darkMode + tocDepth: $tocDepth + injectCSS: $injectCSS + injectHead: $injectHead + injectBody: $injectBody + ) { responseResult { succeeded errorCode diff --git a/client/graph/admin/theme/theme-query-config.gql b/client/graph/admin/theme/theme-query-config.gql index 27ef6a6a52bd3547a554ef1c48e02ce65742bbce..48a8ead19a8d84c27ce51fb147f8083e2054cf59 100644 --- a/client/graph/admin/theme/theme-query-config.gql +++ b/client/graph/admin/theme/theme-query-config.gql @@ -4,9 +4,10 @@ query { theme iconset darkMode - minTocLevel - tocLevel - tocCollapseLevel + tocDepth { + min + max + } injectCSS injectHead injectBody diff --git a/client/store/page.js b/client/store/page.js index c9f736fd75a807874b0410e8cbe235800565ed71..c187c58e19f5ecfb0d5ce014ba8687da6af0bd8f 100644 --- a/client/store/page.js +++ b/client/store/page.js @@ -17,10 +17,11 @@ const state = { editor: '', mode: '', scriptJs: '', - minTocLevel: 0, - tocLevel: 2, - tocCollapseLevel: 2, - doUseTocDefault: true, + tocDepth: { + min: 1, + max: 2 + }, + useDefaultTocDepth: true, scriptCss: '', effectivePermissions: { comments: { diff --git a/client/themes/default/components/page-toc-item.vue b/client/themes/default/components/page-toc-item.vue index 752e1efdb3fbedde677c722406cd63792329b6e7..9478999fa65f201a437f499df08539f0fb93f3d1 100644 --- a/client/themes/default/components/page-toc-item.vue +++ b/client/themes/default/components/page-toc-item.vue @@ -1,7 +1,7 @@ <template lang="pug"> - div - template(v-if='level >= minTocLevel') - v-list-item(@click='click(item.anchor)', v-if='(item.children.length === 0 && tocCollapseLevel > level) || tocCollapseLevel > level', + .page-toc-item + template(v-if='level >= min') + v-list-item(@click='click(item.anchor)', v-if='(item.children.length === 0 && max > level) || max > level', :key='item.anchor', :class='isNestedLevel ? `pl-9` : `pl-6`') v-icon.pl-0(small, color='grey lighten-1') {{ $vuetify.rtl ? `mdi-chevron-left` : `mdi-chevron-right` }} v-list-item-title.pl-4(v-bind:class='titleClasses') {{item.title}} @@ -10,11 +10,11 @@ v-list-item.pl-0(@click='click(item.anchor)', :key='item.anchor') v-list-item-title(v-bind:class='titleClasses') {{item.title}} template(v-if='item.children.length !== 0', v-for='subItem in item.children') - page-toc-item(:item='subItem', :level='level + 1', :tocLevel='tocLevel', :minTocLevel='minTocLevel', :tocCollapseLevel='tocCollapseLevel') - template(v-if='tocCollapseLevel > level', v-for='subItem in item.children') - page-toc-item(:item='subItem', :level='level + 1', :tocLevel='tocLevel', :minTocLevel='minTocLevel', :tocCollapseLevel='tocCollapseLevel') + page-toc-item(:item='subItem', :level='level + 1', :min='min', :max='max') + template(v-if='max > level', v-for='subItem in item.children') + page-toc-item(:item='subItem', :level='level + 1', :min='min', :max='max') template(v-else, v-for='subItem in item.children') - page-toc-item(:item='subItem', :level='level + 1', :tocLevel='tocLevel', :minTocLevel='minTocLevel', :tocCollapseLevel='tocCollapseLevel') + page-toc-item(:item='subItem', :level='level + 1', :min='min', :max='max') </template> <script> @@ -26,15 +26,11 @@ export default { type: Object, default: () => {} }, - minTocLevel: { + min: { type: Number, - default: 0 - }, - tocLevel: { - type: Number, - default: 2 + default: 1 }, - tocCollapseLevel: { + max: { type: Number, default: 2 }, @@ -54,7 +50,7 @@ export default { }, computed: { isNestedLevel() { - return this.level > this.minTocLevel + return this.level > this.min }, titleClasses() { return { @@ -75,7 +71,7 @@ export default { <style lang='scss'> // Hack to fix animations of multi level nesting v-list-group -.v-list-group--sub-group.v-list-group--active .v-list-item:not(.v-list-item--active) .v-list-item__icon.v-list-group__header__prepend-icon .v-icon { +.page-toc-item .v-list-group--sub-group.v-list-group--active .v-list-item:not(.v-list-item--active) .v-list-item__icon.v-list-group__header__prepend-icon .v-icon { transform: rotate(0deg)!important; } </style> diff --git a/client/themes/default/components/page.vue b/client/themes/default/components/page.vue index d2bbd43ec9b055422bd8c650ddc8e739900d9215..9c318c57cb51f9a7d8a628ce2c6604099f06998a 100644 --- a/client/themes/default/components/page.vue +++ b/client/themes/default/components/page.vue @@ -60,8 +60,13 @@ v-card.mb-5(v-if='tocDecoded.length') .overline.pa-5.pb-0(:class='$vuetify.theme.dark ? `blue--text text--lighten-2` : `primary--text`') {{$t('common:page.toc')}} v-list.py-0(dense, nav, :class='$vuetify.theme.dark ? `darken-3-d3` : ``') - template(v-for='item in tocDecoded') - page-toc-item(:item='item', :tocLevel='tocLevel', :minTocLevel='minTocLevel', :tocCollapseLevel='tocCollapseLevel') + page-toc-item( + v-for='(item, idx) in tocDecoded' + :key='`tocitem-` + idx' + :item='item' + :min='tocOptionsDecoded.min' + :max='tocOptionsDecoded.max' + ) v-card.mb-5(v-if='tags.length > 0') .pa-5 .overline.teal--text.pb-2(:class='$vuetify.theme.dark ? `text--lighten-3` : ``') {{$t('common:page.tags')}} @@ -434,21 +439,9 @@ export default { type: Boolean, default: false }, - minTocLevel: { - type: Number, - default: 0 - }, - tocLevel: { - type: Number, - default: 2 - }, - tocCollapseLevel: { - type: Number, - default: 2 - }, - doUseTocDefault: { - type: Boolean, - default: true + tocOptions: { + type: String, + default: '' } }, data() { @@ -518,6 +511,9 @@ export default { tocDecoded () { return JSON.parse(Buffer.from(this.toc, 'base64').toString()) }, + tocOptionsDecoded () { + return JSON.parse(Buffer.from(this.tocOptions, 'base64').toString()) + }, hasAdminPermission: get('page/effectivePermissions@system.manage'), hasWritePagesPermission: get('page/effectivePermissions@pages.write'), hasManagePagesPermission: get('page/effectivePermissions@pages.manage'), diff --git a/dev/containers/Dockerfile b/dev/containers/Dockerfile index 7d37f805262ef8e324a4bfe56353f1e4ed6deb6c..34e5c0164d7d40e271eb639e68d4e32be2bddd3a 100644 --- a/dev/containers/Dockerfile +++ b/dev/containers/Dockerfile @@ -5,7 +5,7 @@ FROM node:14 LABEL maintainer "requarks.io" RUN apt-get update && \ - apt-get install -y bash curl git python make g++ nano openssh-server gnupg cmake && \ + apt-get install -y bash curl git python make g++ nano openssh-server gnupg && \ mkdir -p /wiki WORKDIR /wiki diff --git a/server/app/data.yml b/server/app/data.yml index 1e6de4ea12c955e28cd73128ef6323c628325317..3e8f10d1b6b20c4a8877cd53d456a625b4ab68ef 100644 --- a/server/app/data.yml +++ b/server/app/data.yml @@ -55,10 +55,9 @@ defaults: theme: 'default' iconset: 'md' darkMode: false - minTocLevel: 0 - tocLevel: 2 - tocCollapseLevel: 2 - doUseTocDefault: true + tocDepth: + min: 1 + max: 2 auth: autoLogin: false enforce2FA: false diff --git a/server/controllers/common.js b/server/controllers/common.js index 31725b897f9656c79c03312a21f2b5f51a2a8a20..31e055eb0ff3a4d3a391d3b66ff10207d73224b2 100644 --- a/server/controllers/common.js +++ b/server/controllers/common.js @@ -178,6 +178,11 @@ router.get(['/e', '/e/*'], async (req, res, next) => { title: null, description: null, updatedAt: new Date().toISOString(), + tocOptions: { + min: 1, + max: 2, + useDefault: true + }, extra: { css: '', js: '' @@ -504,19 +509,12 @@ router.get('/*', async (req, res, next) => { if (!_.isEmpty(page.extra.js)) { injectCode.body = `${injectCode.body}\n${page.extra.js}` } - const doUseTocDefault = page.doUseTocDefault === true || page.doUseTocDefault === 1 - var tocLevel - var tocCollapseLevel - var minTocLevel - if (doUseTocDefault) { - minTocLevel = WIKI.config.theming.minTocLevel - tocLevel = WIKI.config.theming.tocLevel - tocCollapseLevel = WIKI.config.theming.tocCollapseLevel - } else { - minTocLevel = page.minTocLevel || WIKI.config.theming.minTocLevel - tocLevel = page.tocLevel || WIKI.config.theming.tocLevel - tocCollapseLevel = page.tocCollapseLevel || WIKI.config.theming.tocCollapseLevel - } + + // -> Set TOC display options + const tocOptions = _.get(page, 'tocOptions.useDefault', true) ? { + min: page.tocOptions.min, + max: page.tocOptions.max + } : WIKI.config.theming.tocDepth if (req.query.legacy || req.get('user-agent').indexOf('Trident') >= 0) { // -> Convert page TOC @@ -528,10 +526,6 @@ router.get('/*', async (req, res, next) => { res.render('legacy/page', { page, sidebar, - minTocLevel, - tocLevel, - tocCollapseLevel, - doUseTocDefault, injectCode, isAuthenticated: req.user && req.user.id !== 2 }) @@ -563,10 +557,7 @@ router.get('/*', async (req, res, next) => { res.render('page', { page, sidebar, - minTocLevel, - tocLevel, - tocCollapseLevel, - doUseTocDefault, + tocOptions, injectCode, comments: commentTmpl, effectivePermissions diff --git a/server/db/migrations-sqlite/2.5.13.js b/server/db/migrations-sqlite/2.5.13.js deleted file mode 100644 index 35d7f60dd75bf37590435b01834fbc8c52bb6e9a..0000000000000000000000000000000000000000 --- a/server/db/migrations-sqlite/2.5.13.js +++ /dev/null @@ -1,11 +0,0 @@ -exports.up = async knex => { - await knex.schema - .alterTable('pages', table => { - table.integer('minTocLevel').notNullable().defaultTo(0) - table.integer('tocLevel').notNullable().defaultTo(0) - table.integer('tocCollapseLevel').notNullable().defaultTo(0) - table.boolean('doUseTocDefault').notNullable().defaultTo(true) - }) -} - -exports.down = knex => { } diff --git a/server/db/migrations-sqlite/2.5.284.js b/server/db/migrations-sqlite/2.5.284.js new file mode 100644 index 0000000000000000000000000000000000000000..cd362a52e088024c74030ff038ec842061ff0ecc --- /dev/null +++ b/server/db/migrations-sqlite/2.5.284.js @@ -0,0 +1,12 @@ +exports.up = async knex => { + await knex.schema + .alterTable('pages', table => { + table.json('tocOptions').notNullable().defaultTo(JSON.stringify({ + min: 1, + max: 2, + useDefault: true + })) + }) +} + +exports.down = knex => { } diff --git a/server/db/migrations/2.5.13.js b/server/db/migrations/2.5.13.js deleted file mode 100644 index 35d7f60dd75bf37590435b01834fbc8c52bb6e9a..0000000000000000000000000000000000000000 --- a/server/db/migrations/2.5.13.js +++ /dev/null @@ -1,11 +0,0 @@ -exports.up = async knex => { - await knex.schema - .alterTable('pages', table => { - table.integer('minTocLevel').notNullable().defaultTo(0) - table.integer('tocLevel').notNullable().defaultTo(0) - table.integer('tocCollapseLevel').notNullable().defaultTo(0) - table.boolean('doUseTocDefault').notNullable().defaultTo(true) - }) -} - -exports.down = knex => { } diff --git a/server/db/migrations/2.5.284.js b/server/db/migrations/2.5.284.js new file mode 100644 index 0000000000000000000000000000000000000000..cd362a52e088024c74030ff038ec842061ff0ecc --- /dev/null +++ b/server/db/migrations/2.5.284.js @@ -0,0 +1,12 @@ +exports.up = async knex => { + await knex.schema + .alterTable('pages', table => { + table.json('tocOptions').notNullable().defaultTo(JSON.stringify({ + min: 1, + max: 2, + useDefault: true + })) + }) +} + +exports.down = knex => { } diff --git a/server/graph/resolvers/theming.js b/server/graph/resolvers/theming.js index d5384389d9427bdcf8327a467747bf3a01618fae..faebc91e4f1cdbb957d978fd5caf194a7ac656a8 100644 --- a/server/graph/resolvers/theming.js +++ b/server/graph/resolvers/theming.js @@ -24,9 +24,7 @@ module.exports = { theme: WIKI.config.theming.theme, iconset: WIKI.config.theming.iconset, darkMode: WIKI.config.theming.darkMode, - minTocLevel: WIKI.config.theming.minTocLevel, - tocLevel: WIKI.config.theming.tocLevel, - tocCollapseLevel: WIKI.config.theming.tocCollapseLevel, + tocDepth: WIKI.config.theming.tocDepth, injectCSS: new CleanCSS({ format: 'beautify' }).minify(WIKI.config.theming.injectCSS).styles, injectHead: WIKI.config.theming.injectHead, injectBody: WIKI.config.theming.injectBody @@ -47,9 +45,7 @@ module.exports = { theme: args.theme, iconset: args.iconset, darkMode: args.darkMode, - minTocLevel: args.minTocLevel, - tocLevel: args.tocLevel, - tocCollapseLevel: args.tocCollapseLevel, + tocDepth: args.tocDepth, injectCSS: args.injectCSS || '', injectHead: args.injectHead || '', injectBody: args.injectBody || '' diff --git a/server/graph/schemas/common.graphql b/server/graph/schemas/common.graphql index 50e9cbdc64684859e2cac684417353caa81c9067..3909ddf222a047fd165e8567cd563089be16c7c3 100644 --- a/server/graph/schemas/common.graphql +++ b/server/graph/schemas/common.graphql @@ -21,6 +21,17 @@ input KeyValuePairInput { value: String! } +# Generic Range Value +type Range { + min: Int + max: Int +} + +input RangeInput { + min: Int! + max: Int! +} + # Generic Mutation Response type DefaultResponse { responseResult: ResponseStatus diff --git a/server/graph/schemas/page.graphql b/server/graph/schemas/page.graphql index ee8d219e8feb19c8e64446eb3d53b66cbfe3790e..0a8561c4cd3d3897a1c6a5c45cb03ce9dec5fd81 100644 --- a/server/graph/schemas/page.graphql +++ b/server/graph/schemas/page.graphql @@ -93,10 +93,8 @@ type PageMutation { scriptJs: String tags: [String]! title: String! - minTocLevel: Int - tocLevel: Int - tocCollapseLevel: Int - doUseTocDefault: Boolean + tocDepth: RangeInput + useDefaultTocDepth: Boolean ): PageResponse @auth(requires: ["write:pages", "manage:pages", "manage:system"]) update( @@ -114,10 +112,8 @@ type PageMutation { scriptJs: String tags: [String] title: String - minTocLevel: Int - tocLevel: Int - tocCollapseLevel: Int - doUseTocDefault: Boolean + tocDepth: RangeInput + useDefaultTocDepth: Boolean ): PageResponse @auth(requires: ["write:pages", "manage:pages", "manage:system"]) convert( @@ -183,37 +179,35 @@ type PageMigrationResponse { } type Page { - id: Int! - path: String! - hash: String! - title: String! - description: String! - isPrivate: Boolean! @auth(requires: ["write:pages", "manage:system"]) - isPublished: Boolean! @auth(requires: ["write:pages", "manage:system"]) + id: Int + path: String + hash: String + title: String + description: String + isPrivate: Boolean @auth(requires: ["write:pages", "manage:system"]) + isPublished: Boolean @auth(requires: ["write:pages", "manage:system"]) privateNS: String @auth(requires: ["write:pages", "manage:system"]) - publishStartDate: Date! @auth(requires: ["write:pages", "manage:system"]) - publishEndDate: Date! @auth(requires: ["write:pages", "manage:system"]) - tags: [PageTag]! - content: String! @auth(requires: ["read:source", "write:pages", "manage:system"]) + publishStartDate: Date @auth(requires: ["write:pages", "manage:system"]) + publishEndDate: Date @auth(requires: ["write:pages", "manage:system"]) + tags: [PageTag] + content: String @auth(requires: ["read:source", "write:pages", "manage:system"]) render: String toc: String - minTocLevel: Int! - tocLevel: Int! - tocCollapseLevel: Int! - doUseTocDefault: Boolean! - contentType: String! - createdAt: Date! - updatedAt: Date! - editor: String! @auth(requires: ["write:pages", "manage:system"]) - locale: String! + tocDepth: Range + useDefaultTocDepth: Boolean + contentType: String + createdAt: Date + updatedAt: Date + editor: String @auth(requires: ["write:pages", "manage:system"]) + locale: String scriptCss: String scriptJs: String - authorId: Int! @auth(requires: ["write:pages", "manage:system"]) - authorName: String! @auth(requires: ["write:pages", "manage:system"]) - authorEmail: String! @auth(requires: ["write:pages", "manage:system"]) - creatorId: Int! @auth(requires: ["write:pages", "manage:system"]) - creatorName: String! @auth(requires: ["write:pages", "manage:system"]) - creatorEmail: String! @auth(requires: ["write:pages", "manage:system"]) + authorId: Int @auth(requires: ["write:pages", "manage:system"]) + authorName: String @auth(requires: ["write:pages", "manage:system"]) + authorEmail: String @auth(requires: ["write:pages", "manage:system"]) + creatorId: Int @auth(requires: ["write:pages", "manage:system"]) + creatorName: String @auth(requires: ["write:pages", "manage:system"]) + creatorEmail: String @auth(requires: ["write:pages", "manage:system"]) } type PageTag { diff --git a/server/graph/schemas/theming.graphql b/server/graph/schemas/theming.graphql index aa5454ab4edede4ba9e1066e8ce9d475bda06af6..610e3fd5c232200a9081b68c2f836f8294cb8905 100644 --- a/server/graph/schemas/theming.graphql +++ b/server/graph/schemas/theming.graphql @@ -28,9 +28,7 @@ type ThemingMutation { theme: String! iconset: String! darkMode: Boolean! - minTocLevel: Int! - tocLevel: Int! - tocCollapseLevel: Int! + tocDepth: RangeInput! injectCSS: String injectHead: String injectBody: String @@ -42,12 +40,10 @@ type ThemingMutation { # ----------------------------------------------- type ThemingConfig { - theme: String! - iconset: String! - darkMode: Boolean! - minTocLevel: Int! - tocLevel: Int! - tocCollapseLevel: Int! + theme: String + iconset: String + darkMode: Boolean + tocDepth: Range injectCSS: String injectHead: String injectBody: String diff --git a/server/models/pages.js b/server/models/pages.js index 3e854972f7ff4b86d25c85107b95da4fcf4c6ebf..28dcc12f3ac0f5dfaf25810945f5484679e2cff8 100644 --- a/server/models/pages.js +++ b/server/models/pages.js @@ -47,10 +47,6 @@ module.exports = class Page extends Model { publishEndDate: {type: 'string'}, content: {type: 'string'}, contentType: {type: 'string'}, - minTocLevel: {type: 'integer'}, - tocLevel: {type: 'integer'}, - tocCollapseLevel: {type: 'integer'}, - doUseTocDefault: {type: 'boolean'}, createdAt: {type: 'string'}, updatedAt: {type: 'string'} } @@ -58,7 +54,7 @@ module.exports = class Page extends Model { } static get jsonAttributes() { - return ['extra'] + return ['extra', 'tocOptions'] } static get relationMappings() { @@ -164,10 +160,11 @@ module.exports = class Page extends Model { }, title: 'string', toc: 'string', - minTocLevel: 'uint', - tocLevel: 'uint', - tocCollapseLevel: 'uint', - doUseTocDefault: 'boolean', + tocOptions: { + min: 'uint', + max: 'uint', + useDefault: 'boolean' + }, updatedAt: 'string' }) } @@ -318,10 +315,11 @@ module.exports = class Page extends Model { publishStartDate: opts.publishStartDate || '', title: opts.title, toc: '[]', - minTocLevel: opts.minTocLevel || 0, - tocLevel: opts.tocLevel || 1, - tocCollapseLevel: opts.tocCollapseLevel || 0, - doUseTocDefault: opts.doUseTocDefault || true, + tocOptions: JSON.stringify({ + min: _.get(opts, 'tocDepth.min', 1), + max: _.get(opts, 'tocDepth.max', 2), + useDefault: opts.useDefaultTocDepth !== false + }), extra: JSON.stringify({ js: scriptJs, css: scriptCss @@ -441,10 +439,11 @@ module.exports = class Page extends Model { publishEndDate: opts.publishEndDate || '', publishStartDate: opts.publishStartDate || '', title: opts.title, - minTocLevel: opts.minTocLevel || 0, - tocLevel: opts.tocLevel || 1, - tocCollapseLevel: opts.tocCollapseLevel || 0, - doUseTocDefault: opts.doUseTocDefault === true || opts.doUseTocDefault === 1, + tocOptions: JSON.stringify({ + min: _.get(opts, 'tocDepth.min', ogPage.tocOptions.min || 1), + max: _.get(opts, 'tocDepth.max', ogPage.tocOptions.max || 2), + useDefault: _.get(opts, 'useDefaultTocDepth', ogPage.tocOptions.useDefault !== false) + }), extra: JSON.stringify({ ...ogPage.extra, js: scriptJs, @@ -802,7 +801,7 @@ module.exports = class Page extends Model { * @returns {Promise} Promise with no value */ static async deletePage(opts) { - const page = await WIKI.models.pages.getPageFromDb(_.has(opts, 'id') ? opts.id : opts); + const page = await WIKI.models.pages.getPageFromDb(_.has(opts, 'id') ? opts.id : opts) if (!page) { throw new WIKI.Error.PageNotFound() } @@ -1006,10 +1005,7 @@ module.exports = class Page extends Model { 'pages.content', 'pages.render', 'pages.toc', - 'pages.minTocLevel', - 'pages.tocLevel', - 'pages.tocCollapseLevel', - 'pages.doUseTocDefault', + 'pages.tocOptions', 'pages.contentType', 'pages.createdAt', 'pages.updatedAt', @@ -1090,10 +1086,7 @@ module.exports = class Page extends Model { tags: page.tags.map(t => _.pick(t, ['tag', 'title'])), title: page.title, toc: _.isString(page.toc) ? page.toc : JSON.stringify(page.toc), - minTocLevel: page.minTocLevel, - tocLevel: page.tocLevel, - tocCollapseLevel: page.tocCollapseLevel, - doUseTocDefault: page.doUseTocDefault, + tocOptions: page.tocOptions, updatedAt: page.updatedAt })) } diff --git a/server/modules/storage/disk/common.js b/server/modules/storage/disk/common.js index 157ab927ee8b62a9089e391b61b941f067a3a758..9ed0b9863d99f74d39709226b36047c85adfc553 100644 --- a/server/modules/storage/disk/common.js +++ b/server/modules/storage/disk/common.js @@ -92,10 +92,6 @@ module.exports = { isPublished: _.get(pageData, 'isPublished', currentPage.isPublished), isPrivate: false, content: pageData.content, - minTocLevel: pageData.minTocLevel, - tocLevel: pageData.tocLevel, - tocCollapseLevel: pageData.tocCollapseLevel, - doUseTocDefault: pageData.doUseTocDefault, user: user, skipStorage: true }) @@ -114,10 +110,7 @@ module.exports = { content: pageData.content, user: user, editor: pageEditor, - skipStorage: true, - tocLevel: pageData.tocLevel, - tocCollapseLevel: pageData.tocCollapseLevel, - doUseTocDefault: pageData.doUseTocDefault + skipStorage: true }) } }, diff --git a/server/setup.js b/server/setup.js index c56d85c03e06887f2960662f0ed5db7ad1da2383..124549a98cf2d1d2709eeb9c7a38e21c9bced39e 100644 --- a/server/setup.js +++ b/server/setup.js @@ -126,11 +126,11 @@ module.exports = () => { _.set(WIKI.config, 'theming', { theme: 'default', darkMode: false, - minTocLevel: 0, - tocLevel: 2, - tocCollapseLevel: 2, - doUseTocDefault: true, iconset: 'mdi', + tocDepth: { + min: 1, + max: 2 + }, injectCSS: '', injectHead: '', injectBody: '' diff --git a/server/views/editor.pug b/server/views/editor.pug index 47d0a1c0476d520f9a6f38847febed92e461c53f..2ea9b19f8c5576a185d6e9e845a26c438871ab30 100644 --- a/server/views/editor.pug +++ b/server/views/editor.pug @@ -19,10 +19,7 @@ block body script-css=page.extra.css script-js=page.extra.js init-mode=page.mode - :min-toc-level=page.minTocLevel - :toc-level=page.tocLevel - :toc-collapse-level=page.tocCollapseLevel - :do-use-toc-default=page.doUseTocDefault.toString() + toc-options=Buffer.from(JSON.stringify(page.tocOptions)).toString('base64') init-editor=page.editorKey init-content=page.content checkout-date=page.updatedAt diff --git a/server/views/page.pug b/server/views/page.pug index 1f81969afb58245e2ec86dfa0f9ad82e0226aaf4..e7414508cb8a233ee98792facec2fd6dacfffe39 100644 --- a/server/views/page.pug +++ b/server/views/page.pug @@ -25,14 +25,11 @@ block body toc=Buffer.from(page.toc).toString('base64') :page-id=page.id sidebar=Buffer.from(JSON.stringify(sidebar)).toString('base64') + toc-options=Buffer.from(JSON.stringify(tocOptions)).toString('base64') nav-mode=config.nav.mode comments-enabled=config.features.featurePageComments effective-permissions=Buffer.from(JSON.stringify(effectivePermissions)).toString('base64') comments-external=comments.codeTemplate - :min-toc-level=minTocLevel - :toc-level=tocLevel - :toc-collapse-level=tocCollapseLevel - :do-use-toc-default=doUseTocDefault.toString() ) template(slot='contents') div!= page.render