From 8715cd69b2da5bc4332a6eda07945fad8738c8ac Mon Sep 17 00:00:00 2001 From: NGPixel Date: Tue, 20 Sep 2022 16:55:05 -0400 Subject: [PATCH] feat: edit shortcuts --- client/components/admin/admin-general.vue | 111 +++++++++++++++++++++- client/store/page.js | 11 ++- client/themes/default/components/page.vue | 82 +++++++++++++++- client/themes/default/scss/app.scss | 4 + server/app/data.yml | 8 ++ server/controllers/common.js | 7 +- server/graph/resolvers/site.js | 13 ++- server/graph/schemas/site.graphql | 14 +++ server/models/pages.js | 4 +- server/views/page.pug | 2 + 10 files changed, 246 insertions(+), 10 deletions(-) diff --git a/client/components/admin/admin-general.vue b/client/components/admin/admin-general.vue index 85561381cc..8804ee3922 100644 --- a/client/components/admin/admin-general.vue +++ b/client/components/admin/admin-general.vue @@ -144,7 +144,7 @@ //- ) //- v-divider.mt-3 - v-switch( + v-switch.mt-0( inset label='Comments' color='indigo' @@ -177,6 +177,76 @@ persistent-hint ) + v-card.mt-5.animated.fadeInUp.wait-p7s + v-toolbar(color='primary', dark, dense, flat) + v-toolbar-title.subtitle-1 {{$t('admin:general.editShortcuts')}} + v-card-text + v-switch.mt-0( + inset + :label='$t(`admin:general.editFab`)' + color='primary' + v-model='config.editFab' + persistent-hint + :hint='$t(`admin:general.editFabHint`)' + ) + v-divider + .overline.grey--text.pa-4 {{$t('admin:general.editMenuBar')}} + .px-3.pb-3 + v-switch.mt-0.ml-1( + inset + :label='$t(`admin:general.displayEditMenuBar`)' + color='primary' + v-model='config.editMenuBar' + persistent-hint + :hint='$t(`admin:general.displayEditMenuBarHint`)' + ) + v-switch.mt-4.ml-1( + v-if='config.editMenuBar' + inset + :label='$t(`admin:general.displayEditMenuBtn`)' + color='primary' + v-model='config.editMenuBtn' + persistent-hint + :hint='$t(`admin:general.displayEditMenuBtnHint`)' + ) + v-switch.mt-4.ml-1( + v-if='config.editMenuBar' + inset + :label='$t(`admin:general.displayEditMenuExternalBtn`)' + color='primary' + v-model='config.editMenuExternalBtn' + persistent-hint + :hint='$t(`admin:general.displayEditMenuExternalBtnHint`)' + ) + template(v-if='config.editMenuBar && config.editMenuExternalBtn') + v-divider + .overline.grey--text.pa-4 External Edit Button + .px-3.pb-3 + v-text-field( + outlined + :label='$t(`admin:general.editMenuExternalName`)' + v-model='config.editMenuExternalName' + prepend-icon='mdi-format-title' + :hint='$t(`admin:general.editMenuExternalNameHint`)' + persistent-hint + ) + v-text-field.mt-3( + outlined + :label='$t(`admin:general.editMenuExternalIcon`)' + v-model='config.editMenuExternalIcon' + prepend-icon='mdi-dice-5' + :hint='$t(`admin:general.editMenuExternalIconHint`)' + persistent-hint + ) + v-text-field.mt-3( + outlined + :label='$t(`admin:general.editMenuExternalUrl`)' + v-model='config.editMenuExternalUrl' + prepend-icon='mdi-near-me' + :hint='$t(`admin:general.editMenuExternalUrlHint`)' + persistent-hint + ) + component(:is='activeModal') @@ -216,7 +286,14 @@ export default { featurePageComments: false, featurePersonalWikis: false, featureTinyPNG: false, - pageExtensions: '' + pageExtensions: '', + editFab: false, + editMenuBar: false, + editMenuBtn: false, + editMenuExternalBtn: false, + editMenuExternalName: '', + editMenuExternalIcon: '', + editMenuExternalUrl: '' }, metaRobots: [ { text: 'Index', value: 'index' }, @@ -274,6 +351,13 @@ export default { $featurePageRatings: Boolean $featurePageComments: Boolean $featurePersonalWikis: Boolean + $editFab: Boolean + $editMenuBar: Boolean + $editMenuBtn: Boolean + $editMenuExternalBtn: Boolean + $editMenuExternalName: String + $editMenuExternalIcon: String + $editMenuExternalUrl: String ) { site { updateConfig( @@ -290,6 +374,13 @@ export default { featurePageRatings: $featurePageRatings featurePageComments: $featurePageComments featurePersonalWikis: $featurePersonalWikis + editFab: $editFab + editMenuBar: $editMenuBar + editMenuBtn: $editMenuBtn + editMenuExternalBtn: $editMenuExternalBtn + editMenuExternalName: $editMenuExternalName + editMenuExternalIcon: $editMenuExternalIcon + editMenuExternalUrl: $editMenuExternalUrl ) { responseResult { succeeded @@ -314,7 +405,14 @@ export default { pageExtensions: _.get(this.config, 'pageExtensions', ''), featurePageRatings: _.get(this.config, 'featurePageRatings', false), featurePageComments: _.get(this.config, 'featurePageComments', false), - featurePersonalWikis: _.get(this.config, 'featurePersonalWikis', false) + featurePersonalWikis: _.get(this.config, 'featurePersonalWikis', false), + editFab: _.get(this.config, 'editFab', false), + editMenuBar: _.get(this.config, 'editMenuBar', false), + editMenuBtn: _.get(this.config, 'editMenuBtn', false), + editMenuExternalBtn: _.get(this.config, 'editMenuExternalBtn', false), + editMenuExternalName: _.get(this.config, 'editMenuExternalName', ''), + editMenuExternalIcon: _.get(this.config, 'editMenuExternalIcon', ''), + editMenuExternalUrl: _.get(this.config, 'editMenuExternalUrl', '') }, watchLoading (isLoading) { this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-site-update') @@ -368,6 +466,13 @@ export default { featurePageRatings featurePageComments featurePersonalWikis + editFab + editMenuBar + editMenuBtn + editMenuExternalBtn + editMenuExternalName + editMenuExternalIcon + editMenuExternalUrl } } } diff --git a/client/store/page.js b/client/store/page.js index 0a72798eee..92979647b8 100644 --- a/client/store/page.js +++ b/client/store/page.js @@ -41,7 +41,16 @@ const state = { manage: false } }, - commentsCount: 0 + commentsCount: 0, + editShortcuts: { + editFab: false, + editMenuBar: false, + editMenuBtn: false, + editMenuExternalBtn: false, + editMenuExternalName: '', + editMenuExternalIcon: '', + editMenuExternalUrl: '' + } } export default { diff --git a/client/themes/default/components/page.vue b/client/themes/default/components/page.vue index 42d0b2c502..0993c9ec43 100644 --- a/client/themes/default/components/page.vue +++ b/client/themes/default/components/page.vue @@ -49,10 +49,28 @@ status-indicator.ml-3(negative, pulse) v-divider v-container.grey.pa-0(fluid, :class='$vuetify.theme.dark ? `darken-4-l3` : `lighten-4`') - v-row(no-gutters, align-content='center', style='height: 90px;') + v-row.page-header-section(no-gutters, align-content='center', style='height: 90px;') v-col.page-col-content.is-page-header(offset-xl='2', offset-lg='3', style='margin-top: auto; margin-bottom: auto;', :class='$vuetify.rtl ? `pr-4` : `pl-4`') .headline.grey--text(:class='$vuetify.theme.dark ? `text--lighten-2` : `text--darken-3`') {{title}} .caption.grey--text.text--darken-1 {{description}} + .page-edit-shortcuts(v-if='editShortcutsObj.editMenuBar') + v-btn( + v-if='editShortcutsObj.editMenuBtn' + @click='pageEdit' + depressed + small + ) + v-icon.mr-2(small) mdi-pencil + span.text-none {{$t(`common:actions.edit`)}} + v-btn( + v-if='editShortcutsObj.editMenuExternalBtn' + :href='editMenuExternalUrl' + target='_blank' + depressed + small + ) + v-icon.mr-2(small) {{ editShortcutsObj.editMenuExternalIcon }} + span.text-none {{$t(`common:page.editExternal`, { name: editShortcutsObj.editMenuExternalName })}} v-divider v-container.pl-5.pt-4(fluid, grid-list-xl) v-layout(row) @@ -186,7 +204,7 @@ v-spacer v-flex.page-col-content(xs12, lg9, xl10) - v-tooltip(:right='$vuetify.rtl', :left='!$vuetify.rtl', v-if='hasAnyPagePermissions') + v-tooltip(:right='$vuetify.rtl', :left='!$vuetify.rtl', v-if='hasAnyPagePermissions && editShortcutsObj.editFab') template(v-slot:activator='{ on: onEditActivator }') v-speed-dial( v-model='pageEditFab' @@ -440,6 +458,14 @@ export default { commentsExternal: { type: Boolean, default: false + }, + editShortcuts: { + type: String, + default: '' + }, + filename: { + type: String, + default: '' } }, data() { @@ -478,6 +504,7 @@ export default { isAuthenticated: get('user/authenticated'), commentsCount: get('page/commentsCount'), commentsPerms: get('page/effectivePermissions@comments'), + editShortcutsObj: get('page/editShortcuts'), rating: { get () { return 3.5 @@ -519,7 +546,14 @@ export default { return this.hasAdminPermission || this.hasWritePagesPermission || this.hasManagePagesPermission || this.hasDeletePagesPermission || this.hasReadSourcePermission || this.hasReadHistoryPermission }, - printView: sync('site/printView') + printView: sync('site/printView'), + editMenuExternalUrl () { + if (this.editShortcutsObj.editMenuBar && this.editShortcutsObj.editMenuExternalBtn) { + return this.editShortcutsObj.editMenuExternalUrl.replace('{filename}', this.filename) + } else { + return '' + } + } }, created() { this.$store.set('page/authorId', this.authorId) @@ -537,6 +571,9 @@ export default { if (this.effectivePermissions) { this.$store.set('page/effectivePermissions', JSON.parse(Buffer.from(this.effectivePermissions, 'base64').toString())) } + if (this.editShortcuts) { + this.$store.set('page/editShortcuts', JSON.parse(Buffer.from(this.editShortcuts, 'base64').toString())) + } this.$store.set('page/mode', 'view') }, @@ -676,4 +713,43 @@ export default { display: none; } +.page-header-section { + position: relative; + + .page-edit-shortcuts { + position: absolute; + bottom: -14px; + right: 10px; + + .v-btn { + border-right: 1px solid #DDD !important; + border-bottom: 1px solid #DDD !important; + border-radius: 0; + color: #777; + background-color: #FFF !important; + + @at-root .theme--dark & { + background-color: #222 !important; + border-right-color: #444 !important; + border-bottom-color: #444 !important; + color: #CCC; + } + + .v-icon { + color: mc('blue', '700'); + } + + &:first-child { + border-top-left-radius: 5px; + border-bottom-left-radius: 5px; + } + + &:last-child { + border-top-right-radius: 5px; + border-bottom-right-radius: 5px; + } + } + } +} + diff --git a/client/themes/default/scss/app.scss b/client/themes/default/scss/app.scss index 2b51ed7202..99f406db4a 100644 --- a/client/themes/default/scss/app.scss +++ b/client/themes/default/scss/app.scss @@ -1036,4 +1036,8 @@ .comments-container { display: none; } + + .page-edit-shortcuts { + display: none; + } } diff --git a/server/app/data.yml b/server/app/data.yml index c122145e0a..54482c99f1 100644 --- a/server/app/data.yml +++ b/server/app/data.yml @@ -67,6 +67,14 @@ defaults: audience: 'urn:wiki.js' tokenExpiration: '30m' tokenRenewal: '14d' + editShortcuts: + editFab: true + editMenuBar: false + editMenuBtn: true + editMenuExternalBtn: true + editMenuExternalName: 'GitHub' + editMenuExternalIcon: 'mdi-github' + editMenuExternalUrl: 'https://github.com/org/repo/blob/main/{filename}' features: featurePageRatings: true featurePageComments: true diff --git a/server/controllers/common.js b/server/controllers/common.js index 0cd47db22b..3bcfcd940f 100644 --- a/server/controllers/common.js +++ b/server/controllers/common.js @@ -542,13 +542,18 @@ router.get('/*', async (req, res, next) => { }) } + // -> Page Filename (for edit on external repo button) + let pageFilename = WIKI.config.lang.namespacing ? `${pageArgs.locale}/${page.path}` : page.path + pageFilename += page.contentType === 'markdown' ? '.md' : '.html' + // -> Render view res.render('page', { page, sidebar, injectCode, comments: commentTmpl, - effectivePermissions + effectivePermissions, + pageFilename }) } } else if (pageArgs.path === 'home') { diff --git a/server/graph/resolvers/site.js b/server/graph/resolvers/site.js index 255bef525d..db538a217f 100644 --- a/server/graph/resolvers/site.js +++ b/server/graph/resolvers/site.js @@ -20,6 +20,7 @@ module.exports = { logoUrl: WIKI.config.logoUrl, pageExtensions: WIKI.config.pageExtensions.join(', '), ...WIKI.config.seo, + ...WIKI.config.editShortcuts, ...WIKI.config.features, ...WIKI.config.security, authAutoLogin: WIKI.config.auth.autoLogin, @@ -84,6 +85,16 @@ module.exports = { tokenRenewal: _.get(args, 'authJwtRenewablePeriod', WIKI.config.auth.tokenRenewal) } + WIKI.config.editShortcuts = { + editFab: _.get(args, 'editFab', WIKI.config.editShortcuts.editFab), + editMenuBar: _.get(args, 'editMenuBar', WIKI.config.editShortcuts.editMenuBar), + editMenuBtn: _.get(args, 'editMenuBtn', WIKI.config.editShortcuts.editMenuBtn), + editMenuExternalBtn: _.get(args, 'editMenuExternalBtn', WIKI.config.editShortcuts.editMenuExternalBtn), + editMenuExternalName: _.get(args, 'editMenuExternalName', WIKI.config.editShortcuts.editMenuExternalName), + editMenuExternalIcon: _.get(args, 'editMenuExternalIcon', WIKI.config.editShortcuts.editMenuExternalIcon), + editMenuExternalUrl: _.get(args, 'editMenuExternalUrl', WIKI.config.editShortcuts.editMenuExternalUrl) + } + WIKI.config.features = { featurePageRatings: _.get(args, 'featurePageRatings', WIKI.config.features.featurePageRatings), featurePageComments: _.get(args, 'featurePageComments', WIKI.config.features.featurePageComments), @@ -109,7 +120,7 @@ module.exports = { forceDownload: _.get(args, 'uploadForceDownload', WIKI.config.uploads.forceDownload) } - await WIKI.configSvc.saveToDb(['host', 'title', 'company', 'contentLicense', 'seo', 'logoUrl', 'pageExtensions', 'auth', 'features', 'security', 'uploads']) + await WIKI.configSvc.saveToDb(['host', 'title', 'company', 'contentLicense', 'seo', 'logoUrl', 'pageExtensions', 'auth', 'editShortcuts', 'features', 'security', 'uploads']) if (WIKI.config.security.securityTrustProxy) { WIKI.app.enable('trust proxy') diff --git a/server/graph/schemas/site.graphql b/server/graph/schemas/site.graphql index 9ff3663985..4cd36d7643 100644 --- a/server/graph/schemas/site.graphql +++ b/server/graph/schemas/site.graphql @@ -41,6 +41,13 @@ type SiteMutation { authJwtAudience: String authJwtExpiration: String authJwtRenewablePeriod: String + editFab: Boolean + editMenuBar: Boolean + editMenuBtn: Boolean + editMenuExternalBtn: Boolean + editMenuExternalName: String + editMenuExternalIcon: String + editMenuExternalUrl: String featurePageRatings: Boolean featurePageComments: Boolean featurePersonalWikis: Boolean @@ -83,6 +90,13 @@ type SiteConfig { authJwtAudience: String authJwtExpiration: String authJwtRenewablePeriod: String + editFab: Boolean + editMenuBar: Boolean + editMenuBtn: Boolean + editMenuExternalBtn: Boolean + editMenuExternalName: String + editMenuExternalIcon: String + editMenuExternalUrl: String featurePageRatings: Boolean featurePageComments: Boolean featurePersonalWikis: Boolean diff --git a/server/models/pages.js b/server/models/pages.js index 44b3825fe3..9bb5d3975c 100644 --- a/server/models/pages.js +++ b/server/models/pages.js @@ -148,6 +148,7 @@ module.exports = class Page extends Model { isPublished: 'boolean', publishEndDate: 'string', publishStartDate: 'string', + contentType: 'string', render: 'string', tags: [ { @@ -787,7 +788,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() } @@ -1067,6 +1068,7 @@ module.exports = class Page extends Model { isPublished: page.isPublished === 1 || page.isPublished === true, publishEndDate: page.publishEndDate, publishStartDate: page.publishStartDate, + contentType: page.contentType, render: page.render, tags: page.tags.map(t => _.pick(t, ['tag', 'title'])), title: page.title, diff --git a/server/views/page.pug b/server/views/page.pug index cc19fbaef2..32649fbe74 100644 --- a/server/views/page.pug +++ b/server/views/page.pug @@ -29,6 +29,8 @@ block body comments-enabled=config.features.featurePageComments effective-permissions=Buffer.from(JSON.stringify(effectivePermissions)).toString('base64') comments-external=comments.codeTemplate + edit-shortcuts=Buffer.from(JSON.stringify(config.editShortcuts)).toString('base64') + filename=pageFilename ) template(slot='contents') div!= page.render