From aa5368b2a1b5894768e6c733c6203ebd2d90ae33 Mon Sep 17 00:00:00 2001 From: NGPixel Date: Wed, 18 Dec 2019 23:45:19 -0500 Subject: [PATCH] feat: katex + admin SSL UI (wip) + dev warning --- client/components/admin.vue | 42 +- client/components/admin/admin-dashboard.vue | 8 +- client/components/admin/admin-general.vue | 70 +- client/components/admin/admin-rendering.vue | 1 - client/components/admin/admin-ssl.vue | 173 +++++ client/components/common/nav-header.vue | 84 ++- .../components/editor/editor-modal-media.vue | 14 + .../admin/site/site-mutation-save-config.gql | 6 +- client/graph/admin/site/site-query-config.gql | 3 +- client/scss/app.scss | 2 + client/scss/components/katex.scss | 4 + client/static/svg/icon-validation.svg | 1 + client/store/site.js | 1 + dev/templates/master.pug | 8 +- package.json | 72 +- server/app/data.yml | 1 + server/core/config.js | 1 + server/graph/resolvers/site.js | 9 +- server/graph/schemas/site.graphql | 6 +- server/master.js | 4 +- .../rendering/html-security/renderer.js | 54 +- .../rendering/markdown-katex/definition.yml | 20 + .../rendering/markdown-katex/renderer.js | 181 +++++ yarn.lock | 657 ++++++++++++------ 24 files changed, 1076 insertions(+), 346 deletions(-) create mode 100644 client/components/admin/admin-ssl.vue create mode 100644 client/scss/components/katex.scss create mode 100644 client/static/svg/icon-validation.svg create mode 100644 server/modules/rendering/markdown-katex/definition.yml create mode 100644 server/modules/rendering/markdown-katex/renderer.js diff --git a/client/components/admin.vue b/client/components/admin.vue index 6de48bcf..8691a1e7 100644 --- a/client/components/admin.vue +++ b/client/components/admin.vue @@ -8,22 +8,22 @@ v-navigation-drawer.pb-0.admin-sidebar(v-model='adminDrawerShown', app, fixed, clipped, :right='$vuetify.rtl', permanent, width='300') vue-scroll(:ops='scrollStyle') v-list(dense, nav) - v-list-item(to='/dashboard') + v-list-item(to='/dashboard', color='primary') v-list-item-avatar(size='24'): v-icon mdi-view-dashboard-variant v-list-item-title {{ $t('admin:dashboard.title') }} template(v-if='hasPermission([`manage:system`, `manage:navigation`, `write:pages`, `manage:pages`, `delete:pages`])') v-divider.my-2 v-subheader.pl-4 {{ $t('admin:nav.site') }} - v-list-item(to='/general', v-if='hasPermission(`manage:system`)') + v-list-item(to='/general', color='primary', v-if='hasPermission(`manage:system`)') v-list-item-avatar(size='24'): v-icon mdi-widgets v-list-item-title {{ $t('admin:general.title') }} - v-list-item(to='/locale', v-if='hasPermission(`manage:system`)') + v-list-item(to='/locale', color='primary', v-if='hasPermission(`manage:system`)') v-list-item-avatar(size='24'): v-icon mdi-web v-list-item-title {{ $t('admin:locale.title') }} - v-list-item(to='/navigation', v-if='hasPermission([`manage:system`, `manage:navigation`])') + v-list-item(to='/navigation', color='primary', v-if='hasPermission([`manage:system`, `manage:navigation`])') v-list-item-avatar(size='24'): v-icon mdi-near-me v-list-item-title {{ $t('admin:navigation.title') }} - v-list-item(to='/pages', v-if='hasPermission([`manage:system`, `write:pages`, `manage:pages`, `delete:pages`])') + v-list-item(to='/pages', color='primary', v-if='hasPermission([`manage:system`, `write:pages`, `manage:pages`, `delete:pages`])') v-list-item-avatar(size='24'): v-icon mdi-file-document-outline v-list-item-title {{ $t('admin:pages.title') }} v-list-item-action(style='min-width:auto;') @@ -32,19 +32,19 @@ v-list-item(to='/tags', v-if='hasPermission([`manage:system`])', disabled) v-list-item-avatar(size='24'): v-icon(color='grey lighten-2') mdi-tag-multiple v-list-item-title {{ $t('admin:tags.title') }} - v-list-item(to='/theme', v-if='hasPermission([`manage:system`, `manage:theme`])') + v-list-item(to='/theme', color='primary', v-if='hasPermission([`manage:system`, `manage:theme`])') v-list-item-avatar(size='24'): v-icon mdi-palette-outline v-list-item-title {{ $t('admin:theme.title') }} template(v-if='hasPermission([`manage:system`, `manage:groups`, `write:groups`, `manage:users`, `write:users`])') v-divider.my-2 v-subheader.pl-4 {{ $t('admin:nav.users') }} - v-list-item(to='/groups', v-if='hasPermission([`manage:system`, `manage:groups`, `write:groups`])') + v-list-item(to='/groups', color='primary', v-if='hasPermission([`manage:system`, `manage:groups`, `write:groups`])') v-list-item-avatar(size='24'): v-icon mdi-account-group v-list-item-title {{ $t('admin:groups.title') }} v-list-item-action(style='min-width:auto;') v-chip(x-small, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`') .caption.grey--text {{ info.groupsTotal }} - v-list-item(to='/users', v-if='hasPermission([`manage:system`, `manage:groups`, `write:groups`, `manage:users`, `write:users`])') + v-list-item(to='/users', color='primary', v-if='hasPermission([`manage:system`, `manage:groups`, `write:groups`, `manage:users`, `write:users`])') v-list-item-avatar(size='24'): v-icon mdi-account-box v-list-item-title {{ $t('admin:users.title') }} v-list-item-action(style='min-width:auto;') @@ -53,10 +53,10 @@ template(v-if='hasPermission(`manage:system`)') v-divider.my-2 v-subheader.pl-4 {{ $t('admin:nav.modules') }} - v-list-item(to='/analytics') + v-list-item(to='/analytics', color='primary') v-list-item-avatar(size='24'): v-icon mdi-chart-timeline-variant v-list-item-title {{ $t('admin:analytics.title') }} - v-list-item(to='/auth') + v-list-item(to='/auth', color='primary') v-list-item-avatar(size='24'): v-icon mdi-lock-outline v-list-item-title {{ $t('admin:auth.title') }} v-list-item(to='/comments', disabled) @@ -68,13 +68,13 @@ v-list-item(to='/logging', disabled) v-list-item-avatar(size='24'): v-icon(color='grey lighten-2') mdi-script-text-outline v-list-item-title {{ $t('admin:logging.title') }} - v-list-item(to='/rendering') + v-list-item(to='/rendering', color='primary') v-list-item-avatar(size='24'): v-icon mdi-cogs v-list-item-title {{ $t('admin:rendering.title') }} - v-list-item(to='/search') + v-list-item(to='/search', color='primary') v-list-item-avatar(size='24'): v-icon mdi-cloud-search-outline v-list-item-title {{ $t('admin:search.title') }} - v-list-item(to='/storage') + v-list-item(to='/storage', color='primary') v-list-item-avatar(size='24'): v-icon mdi-harddisk v-list-item-title {{ $t('admin:storage.title') }} template(v-if='hasPermission([`manage:system`, `manage:api`])') @@ -83,13 +83,16 @@ v-list-item(to='/api', v-if='hasPermission([`manage:system`, `manage:api`])', disabled) v-list-item-avatar(size='24'): v-icon(color='grey lighten-2') mdi-call-split v-list-item-title {{ $t('admin:api.title') }} - v-list-item(to='/mail', v-if='hasPermission(`manage:system`)') + v-list-item(to='/mail', color='primary', v-if='hasPermission(`manage:system`)') v-list-item-avatar(size='24'): v-icon mdi-email-multiple-outline v-list-item-title {{ $t('admin:mail.title') }} - v-list-item(to='/system', v-if='hasPermission(`manage:system`)') + v-list-item(to='/ssl', color='primary', v-if='hasPermission(`manage:system`)') + v-list-item-avatar(size='24'): v-icon mdi-cloud-lock-outline + v-list-item-title {{ $t('admin:ssl.title') }} + v-list-item(to='/system', color='primary', v-if='hasPermission(`manage:system`)') v-list-item-avatar(size='24'): v-icon mdi-tune v-list-item-title {{ $t('admin:system.title') }} - v-list-item(to='/utilities', v-if='hasPermission(`manage:system`)') + v-list-item(to='/utilities', color='primary', v-if='hasPermission(`manage:system`)') v-list-item-avatar(size='24'): v-icon mdi-wrench-outline v-list-item-title {{ $t('admin:utilities.title') }} v-list-item(to='/webhooks', v-if='hasPermission(`manage:system`)', disabled) @@ -104,16 +107,16 @@ v-list-item-avatar(size='24'): v-icon mdi-dev-to v-list-item-title {{ $t('admin:dev.title') }} - v-list-item(to='/dev-flags') + v-list-item(to='/dev-flags', color='primary') v-list-item-title {{ $t('admin:dev.flags.title') }} - v-list-item(href='/graphql') + v-list-item(href='/graphql', color='primary') v-list-item-title GraphQL //- v-list-item(to='/dev-graphiql') //- v-list-item-title {{ $t('admin:dev.graphiql.title') }} //- v-list-item(to='/dev-voyager') //- v-list-item-title {{ $t('admin:dev.voyager.title') }} v-divider.my-2 - v-list-item(to='/contribute') + v-list-item(to='/contribute', color='primary') v-list-item-avatar(size='24'): v-icon mdi-heart-outline v-list-item-title {{ $t('admin:contribute.title') }} @@ -164,6 +167,7 @@ const router = new VueRouter({ { path: '/storage', component: () => import(/* webpackChunkName: "admin" */ './admin/admin-storage.vue') }, { path: '/api', component: () => import(/* webpackChunkName: "admin" */ './admin/admin-api.vue') }, { path: '/mail', component: () => import(/* webpackChunkName: "admin" */ './admin/admin-mail.vue') }, + { path: '/ssl', component: () => import(/* webpackChunkName: "admin" */ './admin/admin-ssl.vue') }, { path: '/system', component: () => import(/* webpackChunkName: "admin" */ './admin/admin-system.vue') }, { path: '/utilities', component: () => import(/* webpackChunkName: "admin" */ './admin/admin-utilities.vue') }, { path: '/webhooks', component: () => import(/* webpackChunkName: "admin" */ './admin/admin-webhooks.vue') }, diff --git a/client/components/admin/admin-dashboard.vue b/client/components/admin/admin-dashboard.vue index d1ba7163..07c7e67b 100644 --- a/client/components/admin/admin-dashboard.vue +++ b/client/components/admin/admin-dashboard.vue @@ -19,7 +19,7 @@ easing='easeOutQuint' ) v-flex(xs12 md6 lg4 xl3 d-flex) - v-card.green.lighten-1.dashboard-card.animated.fadeInUp.wait-p2s(dark) + v-card.blue.darken-3.dashboard-card.animated.fadeInUp.wait-p2s(dark) v-card-text v-icon.dashboard-icon mdi-account .overline {{$t('admin:dashboard.users')}} @@ -30,7 +30,7 @@ easing='easeOutQuint' ) v-flex(xs12 md6 lg4 xl3 d-flex) - v-card.indigo.lighten-1.dashboard-card.animated.fadeInUp.wait-p4s(dark) + v-card.blue.darken-4.dashboard-card.animated.fadeInUp.wait-p4s(dark) v-card-text v-icon.dashboard-icon mdi-account-group .overline {{$t('admin:dashboard.groups')}} @@ -42,11 +42,11 @@ ) v-flex(xs12 md6 lg12 xl3 d-flex) v-card.dashboard-card.animated.fadeInUp.wait-p6s( - :class='isLatestVersion ? "teal lighten-2" : "red lighten-2"' + :class='isLatestVersion ? "green" : "red lighten-2"' dark ) v-btn.btn-animate-wrench(fab, absolute, :right='!$vuetify.rtl', :left='$vuetify.rtl', top, small, light, to='system', v-if='hasPermission(`manage:system`)') - v-icon(:color='isLatestVersion ? `teal` : `red darken-4`', small) mdi-wrench + v-icon(:color='isLatestVersion ? `green` : `red darken-4`', small) mdi-wrench v-card-text v-icon.dashboard-icon mdi-blur .subtitle-1 Wiki.js {{info.currentVersion}} diff --git a/client/components/admin/admin-general.vue b/client/components/admin/admin-general.vue index d1007f3b..b1085ad5 100644 --- a/client/components/admin/admin-general.vue +++ b/client/components/admin/admin-general.vue @@ -41,19 +41,26 @@ persistent-hint ) v-divider - .overline.grey--text.pa-4 {{$t('admin:general.logo')}} #[v-chip.ml-2(label, color='grey', small, outlined) coming soon] - v-card-text.pb-4.pl-5 - v-layout.px-3(row, align-center) - v-avatar(size='100', :color='$vuetify.theme.dark ? `grey darken-2` : `grey lighten-3`', :tile='config.logoIsSquare') - .ml-4 - v-btn.mr-3(color='teal', depressed, disabled) - v-icon(left) mdi-cloud-upload - span {{$t('admin:general.uploadLogo')}} - v-btn(color='teal', depressed, disabled) - v-icon(left) mdi-close - span {{$t('admin:general.uploadClear')}} - .caption.mt-3.grey--text {{$t('admin:general.uploadSizeHint', { size: '120x120' })}} - .caption.grey--text {{$t('admin:general.uploadTypesHint', { typeList: 'SVG, PNG', lastType: 'JPG' })}}. + .overline.grey--text.pa-4 {{$t('admin:general.logo')}} + .pt-2.pb-7.pl-10.pr-3 + .d-flex.align-center + v-avatar(size='100', tile) + v-img( + :src='config.logoUrl' + lazy-src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNcWQ8AAdcBKrJda2oAAAAASUVORK5CYII=' + aspect-ratio='1' + ) + .ml-4(style='flex: 1 1 auto;') + v-text-field( + outlined + :label='$t(`admin:general.logoUrl`)' + v-model='config.logoUrl' + :hint='$t(`admin:general.logoUrlHint`)' + persistent-hint + append-icon='mdi-folder-image' + @click:append='browseLogo' + @keyup.enter='refreshLogo' + ) v-divider .overline.grey--text.pa-4 {{$t('admin:general.footerCopyright')}} .px-3.pb-3 @@ -236,6 +243,7 @@ hint='One directive per line.' disabled ) + component(:is='activeModal') @@ -245,7 +253,17 @@ import { get, sync } from 'vuex-pathify' import siteConfigQuery from 'gql/admin/site/site-query-config.gql' import siteUpdateConfigMutation from 'gql/admin/site/site-mutation-save-config.gql' +import editorStore from '../../store/editor' + +/* global WIKI */ + +WIKI.$store.registerModule('editor', editorStore) + export default { + i18nOptions: { namespaces: 'editor' }, + components: { + editorModalMedia: () => import(/* webpackChunkName: "editor", webpackMode: "lazy" */ '../editor/editor-modal-media.vue') + }, data() { return { analyticsServices: [ @@ -262,8 +280,7 @@ export default { analyticsService: '', analyticsId: '', company: '', - hasLogo: false, - logoIsSquare: false, + logoUrl: '', featureAnalytics: false, featurePageRatings: false, featurePageComments: false, @@ -297,7 +314,9 @@ export default { computed: { darkMode: get('site/dark'), siteTitle: sync('site/title'), - company: sync('site/company') + logoUrl: sync('site/logoUrl'), + company: sync('site/company'), + activeModal: sync('editor/activeModal') }, methods: { async save () { @@ -312,8 +331,7 @@ export default { analyticsService: _.get(this.config, 'analyticsService', ''), analyticsId: _.get(this.config, 'analyticsId', ''), company: _.get(this.config, 'company', ''), - hasLogo: _.get(this.config, 'hasLogo', false), - logoIsSquare: _.get(this.config, 'logoIsSquare', false), + logoUrl: _.get(this.config, 'logoUrl', ''), featurePageRatings: _.get(this.config, 'featurePageRatings', false), featurePageComments: _.get(this.config, 'featurePageComments', false), featurePersonalWikis: _.get(this.config, 'featurePersonalWikis', false), @@ -337,11 +355,27 @@ export default { }) this.siteTitle = this.config.title this.company = this.config.company + this.logoUrl = this.config.logoUrl } catch (err) { this.$store.commit('pushGraphError', err) } + }, + browseLogo () { + this.$store.set('editor/editorKey', 'common') + this.activeModal = 'editorModalMedia' + }, + refreshLogo () { + this.$forceUpdate() } }, + mounted () { + this.$root.$on('editorInsert', opts => { + this.config.logoUrl = opts.path + }) + }, + beforeDestroy() { + this.$root.$off('editorInsert') + }, apollo: { config: { query: siteConfigQuery, diff --git a/client/components/admin/admin-rendering.vue b/client/components/admin/admin-rendering.vue index 03cb4152..e3de4d94 100644 --- a/client/components/admin/admin-rendering.vue +++ b/client/components/admin/admin-rendering.vue @@ -6,7 +6,6 @@ img.animated.fadeInUp(src='/svg/icon-process.svg', alt='Rendering', style='width: 80px;') .admin-header-title .headline.primary--text.animated.fadeInLeft Rendering - .subtitle-1.grey--text.animated.fadeInLeft.wait-p4s Configure how content is rendered #[v-chip(label, color='primary', small).white--text coming soon] v-spacer v-btn.mx-3.animated.fadeInDown.wait-p2s(outlined, color='grey', @click='refresh', large) v-icon mdi-refresh diff --git a/client/components/admin/admin-ssl.vue b/client/components/admin/admin-ssl.vue new file mode 100644 index 00000000..d65503c9 --- /dev/null +++ b/client/components/admin/admin-ssl.vue @@ -0,0 +1,173 @@ + + + + + diff --git a/client/components/common/nav-header.vue b/client/components/common/nav-header.vue index 4a604ecf..aa49fee8 100644 --- a/client/components/common/nav-header.vue +++ b/client/components/common/nav-header.vue @@ -19,28 +19,30 @@ v-layout(row) v-flex(xs6, md4) v-toolbar.nav-header-inner.pl-3(color='black', dark, flat) - v-menu(open-on-hover, offset-y, bottom, left, min-width='250', transition='slide-y-transition') - template(v-slot:activator='{ on }') - v-app-bar-nav-icon.btn-animate-app(v-on='on', :class='$vuetify.rtl ? `mx-0` : ``') - v-icon mdi-menu - v-list(nav, :light='!$vuetify.theme.dark', :dark='$vuetify.theme.dark', :class='$vuetify.theme.dark ? `grey darken-4` : ``') - v-list-item.pl-4(href='/') - v-list-item-avatar(size='24'): v-icon(color='blue') mdi-home - v-list-item-title.body-2 {{$t('common:header.home')}} - v-list-item.pl-4(@click='') - v-list-item-avatar(size='24'): v-icon(color='grey lighten-2') mdi-file-tree - v-list-item-content - v-list-item-title.body-2.grey--text.text--ligten-2 {{$t('common:header.siteMap')}} - v-list-item-subtitle.overline.grey--text.text--lighten-2 Coming soon - v-list-item.pl-4(href='/t') - v-list-item-avatar(size='24'): v-icon(color='teal') mdi-tag-multiple - v-list-item-title.body-2 {{$t('common:header.browseTags')}} - v-list-item.pl-4(@click='assets') - v-list-item-avatar(size='24'): v-icon(color='grey lighten-2') mdi-folder-multiple-image - v-list-item-content - v-list-item-title.body-2.grey--text.text--ligten-2 {{$t('common:header.imagesFiles')}} - v-list-item-subtitle.overline.grey--text.text--lighten-2 Coming soon - v-toolbar-title(:class='{ "mx-2": $vuetify.breakpoint.mdAndUp, "mx-0": $vuetify.breakpoint.smAndDown }') + v-avatar(tile, size='34', @click='goHome') + v-img.org-logo(:src='logoUrl') + //- v-menu(open-on-hover, offset-y, bottom, left, min-width='250', transition='slide-y-transition') + //- template(v-slot:activator='{ on }') + //- v-app-bar-nav-icon.btn-animate-app(v-on='on', :class='$vuetify.rtl ? `mx-0` : ``') + //- v-icon mdi-menu + //- v-list(nav, :light='!$vuetify.theme.dark', :dark='$vuetify.theme.dark', :class='$vuetify.theme.dark ? `grey darken-4` : ``') + //- v-list-item.pl-4(href='/') + //- v-list-item-avatar(size='24'): v-icon(color='blue') mdi-home + //- v-list-item-title.body-2 {{$t('common:header.home')}} + //- v-list-item.pl-4(@click='') + //- v-list-item-avatar(size='24'): v-icon(color='grey lighten-2') mdi-file-tree + //- v-list-item-content + //- v-list-item-title.body-2.grey--text.text--ligten-2 {{$t('common:header.siteMap')}} + //- v-list-item-subtitle.overline.grey--text.text--lighten-2 Coming soon + //- v-list-item.pl-4(href='/t') + //- v-list-item-avatar(size='24'): v-icon(color='teal') mdi-tag-multiple + //- v-list-item-title.body-2 {{$t('common:header.browseTags')}} + //- v-list-item.pl-4(@click='assets') + //- v-list-item-avatar(size='24'): v-icon(color='grey lighten-2') mdi-folder-multiple-image + //- v-list-item-content + //- v-list-item-title.body-2.grey--text.text--ligten-2 {{$t('common:header.imagesFiles')}} + //- v-list-item-subtitle.overline.grey--text.text--lighten-2 Coming soon + v-toolbar-title(:class='{ "mx-3": $vuetify.breakpoint.mdAndUp, "mx-0": $vuetify.breakpoint.smAndDown }') span.subheading {{title}} v-flex(md4, v-if='$vuetify.breakpoint.mdAndUp') v-toolbar.nav-header-inner(color='black', dark, flat) @@ -197,6 +199,12 @@ page-selector(mode='create', v-model='newPageModal', :open-handler='pageNewCreate', :locale='locale') page-selector(mode='move', v-model='movePageModal', :open-handler='pageMoveRename', :path='path', :locale='locale') page-delete(v-model='deletePageModal', v-if='path && path.length') + + .nav-header-dev(v-if='isDevMode') + v-icon mdi-alert + div + .overline DEVELOPMENT VERSION + .overline This code base is NOT for production use!