瀏覽代碼

feat: save + load admin editors config

Nicolas Giard 2 年之前
父節點
當前提交
f849c16d9d

+ 10 - 7
server/db/migrations/3.0.0.js

@@ -579,15 +579,18 @@ exports.up = async knex => {
           isActive: true,
           config: {
             allowHTML: true,
-            linkify: true,
+            kroki: false,
+            krokiServerUrl: 'https://kroki.io',
+            latexEngine: 'katex',
             lineBreaks: true,
-            typographer: false,
-            underline: false,
+            linkify: true,
+            multimdTable: true,
+            plantuml: false,
+            plantumlServerUrl: 'https://www.plantuml.com/plantuml/',
+            quotes: 'english',
             tabWidth: 2,
-            latexEngine: 'katex',
-            kroki: true,
-            plantuml: true,
-            multimdTable: true
+            typographer: false,
+            underline: true
           }
         },
         wysiwyg: {

+ 12 - 0
server/graph/schemas/site.graphql

@@ -186,6 +186,7 @@ input SiteUpdateInput {
   features: SiteFeaturesInput
   defaults: SiteDefaultsInput
   uploads: SiteUploadsInput
+  editors: SiteEditorsInput
   theme: SiteThemeInput
 }
 
@@ -230,6 +231,17 @@ input SiteThemeInput {
   contentFont: String
 }
 
+input SiteEditorsInput {
+  asciidoc: SiteEditorInput
+  markdown: SiteEditorInput
+  wysiwyg: SiteEditorInput
+}
+
+input SiteEditorInput {
+  isActive: Boolean
+  config: JSON
+}
+
 input SiteUploadsInput {
   conflictBehavior: SiteUploadConflictBehavior
   normalizeFilename: Boolean

+ 28 - 0
server/models/sites.js

@@ -107,6 +107,34 @@ module.exports = class Site extends Model {
           baseFont: '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: {
           conflictBehavior: 'overwrite',
           normalizeFilename: true

+ 99 - 2
ux/src/components/EditorMarkdownConfigOverlay.vue

@@ -15,6 +15,16 @@ q-layout(view='hHh lpR fFf', container)
       type='a'
     )
     q-btn-group(push)
+      q-btn(
+        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'
@@ -245,7 +255,9 @@ q-layout(view='hHh lpR fFf', container)
 <script setup>
 import { useI18n } from 'vue-i18n'
 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 { useSiteStore } from 'src/stores/site'
@@ -310,7 +322,92 @@ function close () {
   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>

+ 3 - 1
ux/src/i18n/locales/en.json

@@ -1705,5 +1705,7 @@
   "admin.editors.markdown.plantumlServerUrl": "PlantUML Server URL",
   "admin.editors.markdown.plantumlServerUrlHint": "URL to the PlantUML server used for image generation.",
   "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."
 }

+ 49 - 1
ux/src/pages/AdminEditors.vue

@@ -209,7 +209,55 @@ async function load () {
   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 () {
   await load()