feat: save + load admin editors config

parent 3f3ac4ee
...@@ -579,15 +579,18 @@ exports.up = async knex => { ...@@ -579,15 +579,18 @@ exports.up = async knex => {
isActive: true, isActive: true,
config: { config: {
allowHTML: true, allowHTML: true,
linkify: true, kroki: false,
krokiServerUrl: 'https://kroki.io',
latexEngine: 'katex',
lineBreaks: true, lineBreaks: true,
typographer: false, linkify: true,
underline: false, multimdTable: true,
plantuml: false,
plantumlServerUrl: 'https://www.plantuml.com/plantuml/',
quotes: 'english',
tabWidth: 2, tabWidth: 2,
latexEngine: 'katex', typographer: false,
kroki: true, underline: true
plantuml: true,
multimdTable: true
} }
}, },
wysiwyg: { wysiwyg: {
......
...@@ -186,6 +186,7 @@ input SiteUpdateInput { ...@@ -186,6 +186,7 @@ input SiteUpdateInput {
features: SiteFeaturesInput features: SiteFeaturesInput
defaults: SiteDefaultsInput defaults: SiteDefaultsInput
uploads: SiteUploadsInput uploads: SiteUploadsInput
editors: SiteEditorsInput
theme: SiteThemeInput theme: SiteThemeInput
} }
...@@ -230,6 +231,17 @@ input SiteThemeInput { ...@@ -230,6 +231,17 @@ input SiteThemeInput {
contentFont: String contentFont: String
} }
input SiteEditorsInput {
asciidoc: SiteEditorInput
markdown: SiteEditorInput
wysiwyg: SiteEditorInput
}
input SiteEditorInput {
isActive: Boolean
config: JSON
}
input SiteUploadsInput { input SiteUploadsInput {
conflictBehavior: SiteUploadConflictBehavior conflictBehavior: SiteUploadConflictBehavior
normalizeFilename: Boolean normalizeFilename: Boolean
......
...@@ -107,6 +107,34 @@ module.exports = class Site extends Model { ...@@ -107,6 +107,34 @@ module.exports = class Site extends Model {
baseFont: 'roboto', baseFont: 'roboto',
contentFont: 'roboto' contentFont: 'roboto'
}, },
editors: {
asciidoc: {
isActive: true,
config: {}
},
markdown: {
isActive: true,
config: {
allowHTML: true,
kroki: false,
krokiServerUrl: 'https://kroki.io',
latexEngine: 'katex',
lineBreaks: true,
linkify: true,
multimdTable: true,
plantuml: false,
plantumlServerUrl: 'https://www.plantuml.com/plantuml/',
quotes: 'english',
tabWidth: 2,
typographer: false,
underline: true
}
},
wysiwyg: {
isActive: true,
config: {}
}
},
uploads: { uploads: {
conflictBehavior: 'overwrite', conflictBehavior: 'overwrite',
normalizeFilename: true normalizeFilename: true
......
...@@ -17,6 +17,16 @@ q-layout(view='hHh lpR fFf', container) ...@@ -17,6 +17,16 @@ q-layout(view='hHh lpR fFf', container)
q-btn-group(push) q-btn-group(push)
q-btn( q-btn(
push push
color='grey-6'
text-color='white'
:aria-label='t(`common.actions.refresh`)'
icon='las la-redo-alt'
@click='load'
:loading='state.loading > 0'
)
q-tooltip(anchor='center left', self='center right') {{t(`common.actions.refresh`)}}
q-btn(
push
color='white' color='white'
text-color='grey-7' text-color='grey-7'
:label='t(`common.actions.cancel`)' :label='t(`common.actions.cancel`)'
...@@ -245,7 +255,9 @@ q-layout(view='hHh lpR fFf', container) ...@@ -245,7 +255,9 @@ q-layout(view='hHh lpR fFf', container)
<script setup> <script setup>
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useQuasar } from 'quasar' import { useQuasar } from 'quasar'
import { reactive, ref } from 'vue' import { onMounted, reactive } from 'vue'
import gql from 'graphql-tag'
import { cloneDeep } from 'lodash-es'
import { useAdminStore } from '../stores/admin' import { useAdminStore } from '../stores/admin'
import { useSiteStore } from 'src/stores/site' import { useSiteStore } from 'src/stores/site'
...@@ -310,7 +322,92 @@ function close () { ...@@ -310,7 +322,92 @@ function close () {
adminStore.$patch({ overlay: '' }) adminStore.$patch({ overlay: '' })
} }
async function save () { async function load () {
state.loading++
$q.loading.show()
try {
const resp = await APOLLO_CLIENT.query({
query: gql`
query getEditorsState (
$siteId: UUID!
) {
siteById (
id: $siteId
) {
id
editors {
markdown {
config
}
}
}
}`,
variables: {
siteId: adminStore.currentSiteId
},
fetchPolicy: 'network-only'
})
state.config = cloneDeep(resp?.data?.siteById?.editors?.markdown?.config)
} catch (err) {
$q.notify({
type: 'negative',
message: 'Failed to fetch markdown editor configuration.'
})
}
$q.loading.hide()
state.loading--
}
async function save () {
state.loading++
try {
const respRaw = await APOLLO_CLIENT.mutate({
mutation: gql`
mutation saveEditorState (
$id: UUID!
$patch: SiteUpdateInput!
) {
updateSite (
id: $id,
patch: $patch
) {
operation {
succeeded
slug
message
}
}
}
`,
variables: {
id: adminStore.currentSiteId,
patch: {
editors: {
markdown: { config: state.config }
}
}
}
})
if (respRaw?.data?.updateSite?.operation?.succeeded) {
$q.notify({
type: 'positive',
message: t('admin.editors.markdown.saveSuccess')
})
close()
} else {
throw new Error(respRaw?.data?.updateSite?.operation?.message || 'An unexpected error occured.')
}
} catch (err) {
$q.notify({
type: 'negative',
message: 'Failed to save Markdown editor config',
caption: err.message
})
}
state.loading--
} }
onMounted(() => {
load()
})
</script> </script>
...@@ -1705,5 +1705,7 @@ ...@@ -1705,5 +1705,7 @@
"admin.editors.markdown.plantumlServerUrl": "PlantUML Server URL", "admin.editors.markdown.plantumlServerUrl": "PlantUML Server URL",
"admin.editors.markdown.plantumlServerUrlHint": "URL to the PlantUML server used for image generation.", "admin.editors.markdown.plantumlServerUrlHint": "URL to the PlantUML server used for image generation.",
"admin.editors.markdown.quotes": "Quotes Style", "admin.editors.markdown.quotes": "Quotes Style",
"admin.editors.markdown.quotesHint": "When typographer is enabled. Double + single quotes replacement pairs. e.g. «»„“ for Russian, „“‚‘ for German, etc." "admin.editors.markdown.quotesHint": "When typographer is enabled. Double + single quotes replacement pairs. e.g. «»„“ for Russian, „“‚‘ for German, etc.",
"admin.editors.saveSuccess": "Editors state saved successfully.",
"admin.editors.markdown.saveSuccess": "Markdown editor configuration saved successfully."
} }
...@@ -209,7 +209,55 @@ async function load () { ...@@ -209,7 +209,55 @@ async function load () {
state.loading-- state.loading--
} }
async function save () {} async function save () {
state.loading++
try {
const respRaw = await APOLLO_CLIENT.mutate({
mutation: gql`
mutation saveEditorState (
$id: UUID!
$patch: SiteUpdateInput!
) {
updateSite (
id: $id,
patch: $patch
) {
operation {
succeeded
slug
message
}
}
}
`,
variables: {
id: adminStore.currentSiteId,
patch: {
editors: {
asciidoc: { isActive: state.config.asciidoc },
markdown: { isActive: state.config.markdown },
wysiwyg: { isActive: state.config.wysiwyg }
}
}
}
})
if (respRaw?.data?.updateSite?.operation?.succeeded) {
$q.notify({
type: 'positive',
message: t('admin.editors.saveSuccess')
})
} else {
throw new Error(respRaw?.data?.updateSite?.operation?.message || 'An unexpected error occured.')
}
} catch (err) {
$q.notify({
type: 'negative',
message: 'Failed to save site editors config',
caption: err.message
})
}
state.loading--
}
async function refresh () { async function refresh () {
await load() await load()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment