feat: tags UI (wip) + save tags from page
This commit is contained in:
parent
8e80b7471d
commit
5a7fd2d73e
@ -163,9 +163,10 @@ Vue.component('not-found', () => import(/* webpackChunkName: "not-found" */ './c
|
|||||||
Vue.component('page-selector', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/page-selector.vue'))
|
Vue.component('page-selector', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/page-selector.vue'))
|
||||||
Vue.component('profile', () => import(/* webpackChunkName: "profile" */ './components/profile.vue'))
|
Vue.component('profile', () => import(/* webpackChunkName: "profile" */ './components/profile.vue'))
|
||||||
Vue.component('register', () => import(/* webpackChunkName: "register" */ './components/register.vue'))
|
Vue.component('register', () => import(/* webpackChunkName: "register" */ './components/register.vue'))
|
||||||
Vue.component('v-card-chin', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/v-card-chin.vue'))
|
|
||||||
Vue.component('search-results', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/search-results.vue'))
|
Vue.component('search-results', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/search-results.vue'))
|
||||||
|
Vue.component('tags', () => import(/* webpackChunkName: "tags" */ './components/tags.vue'))
|
||||||
Vue.component('unauthorized', () => import(/* webpackChunkName: "unauthorized" */ './components/unauthorized.vue'))
|
Vue.component('unauthorized', () => import(/* webpackChunkName: "unauthorized" */ './components/unauthorized.vue'))
|
||||||
|
Vue.component('v-card-chin', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/v-card-chin.vue'))
|
||||||
Vue.component('welcome', () => import(/* webpackChunkName: "welcome" */ './components/welcome.vue'))
|
Vue.component('welcome', () => import(/* webpackChunkName: "welcome" */ './components/welcome.vue'))
|
||||||
|
|
||||||
Vue.component('nav-footer', () => import(/* webpackChunkName: "theme-page" */ './themes/' + process.env.CURRENT_THEME + '/components/nav-footer.vue'))
|
Vue.component('nav-footer', () => import(/* webpackChunkName: "theme-page" */ './themes/' + process.env.CURRENT_THEME + '/components/nav-footer.vue'))
|
||||||
|
@ -37,6 +37,15 @@
|
|||||||
v-card.animated.fadeInUp.wait-p2s
|
v-card.animated.fadeInUp.wait-p2s
|
||||||
v-toolbar(color='primary', dense, flat, dark)
|
v-toolbar(color='primary', dense, flat, dark)
|
||||||
.subtitle-1 {{provider.title}}
|
.subtitle-1 {{provider.title}}
|
||||||
|
v-spacer
|
||||||
|
v-switch(
|
||||||
|
dark
|
||||||
|
color='blue lighten-5'
|
||||||
|
label='Active'
|
||||||
|
v-model='provider.isEnabled'
|
||||||
|
hide-details
|
||||||
|
inset
|
||||||
|
)
|
||||||
v-card-text
|
v-card-text
|
||||||
v-form
|
v-form
|
||||||
.analytic-provider-logo
|
.analytic-provider-logo
|
||||||
@ -68,6 +77,7 @@
|
|||||||
prepend-icon='mdi-settings-box'
|
prepend-icon='mdi-settings-box'
|
||||||
:hint='cfg.value.hint ? cfg.value.hint : ""'
|
:hint='cfg.value.hint ? cfg.value.hint : ""'
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
inset
|
||||||
)
|
)
|
||||||
v-textarea(
|
v-textarea(
|
||||||
v-else-if='cfg.value.type === "string" && cfg.value.multiline'
|
v-else-if='cfg.value.type === "string" && cfg.value.multiline'
|
||||||
|
@ -63,10 +63,19 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
v-flex(xs12, lg9)
|
v-flex(xs12, lg9)
|
||||||
|
|
||||||
v-card.animated.fadeInUp.wait-p2s
|
v-card.animated.fadeInUp.wait-p2s
|
||||||
v-toolbar(color='primary', dense, flat, dark)
|
v-toolbar(color='primary', dense, flat, dark)
|
||||||
.subtitle-1 {{strategy.title}}
|
.subtitle-1 {{strategy.title}}
|
||||||
|
v-spacer
|
||||||
|
v-switch(
|
||||||
|
dark
|
||||||
|
color='blue lighten-5'
|
||||||
|
label='Active'
|
||||||
|
v-model='strategy.isEnabled'
|
||||||
|
hide-details
|
||||||
|
inset
|
||||||
|
:disabled='strategy.key === `local`'
|
||||||
|
)
|
||||||
v-card-text
|
v-card-text
|
||||||
v-form
|
v-form
|
||||||
.authlogo
|
.authlogo
|
||||||
@ -104,6 +113,7 @@
|
|||||||
prepend-icon='mdi-settings-box'
|
prepend-icon='mdi-settings-box'
|
||||||
:hint='cfg.value.hint ? cfg.value.hint : ""'
|
:hint='cfg.value.hint ? cfg.value.hint : ""'
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
inset
|
||||||
)
|
)
|
||||||
v-textarea(
|
v-textarea(
|
||||||
v-else-if='cfg.value.type === "string" && cfg.value.multiline'
|
v-else-if='cfg.value.type === "string" && cfg.value.multiline'
|
||||||
@ -136,6 +146,7 @@
|
|||||||
color='primary'
|
color='primary'
|
||||||
:hint='$t(`admin:auth.selfRegistrationHint`)'
|
:hint='$t(`admin:auth.selfRegistrationHint`)'
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
inset
|
||||||
)
|
)
|
||||||
v-switch.ml-3(
|
v-switch.ml-3(
|
||||||
v-if='strategy.key === `local`'
|
v-if='strategy.key === `local`'
|
||||||
@ -145,6 +156,7 @@
|
|||||||
color='primary'
|
color='primary'
|
||||||
hint='Protects against spam robots and malicious registrations.'
|
hint='Protects against spam robots and malicious registrations.'
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
inset
|
||||||
)
|
)
|
||||||
v-combobox.ml-3.mt-3(
|
v-combobox.ml-3.mt-3(
|
||||||
:label='$t(`admin:auth.domainsWhitelist`)'
|
:label='$t(`admin:auth.domainsWhitelist`)'
|
||||||
@ -187,6 +199,7 @@
|
|||||||
color='primary'
|
color='primary'
|
||||||
:hint='$t(`admin:auth.force2faHint`)'
|
:hint='$t(`admin:auth.force2faHint`)'
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
inset
|
||||||
)
|
)
|
||||||
|
|
||||||
v-card.mt-4.wiki-form.animated.fadeInUp.wait-p4s
|
v-card.mt-4.wiki-form.animated.fadeInUp.wait-p4s
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
easing='easeOutQuint'
|
easing='easeOutQuint'
|
||||||
)
|
)
|
||||||
v-flex(xs12 md6 lg4 xl3 d-flex)
|
v-flex(xs12 md6 lg4 xl3 d-flex)
|
||||||
v-card.indigo.lighten-1.dashboard-card.animated.fadeInUp.wait-p2s(dark)
|
v-card.green.lighten-1.dashboard-card.animated.fadeInUp.wait-p2s(dark)
|
||||||
v-card-text
|
v-card-text
|
||||||
v-icon.dashboard-icon mdi-account
|
v-icon.dashboard-icon mdi-account
|
||||||
.overline {{$t('admin:dashboard.users')}}
|
.overline {{$t('admin:dashboard.users')}}
|
||||||
@ -30,7 +30,7 @@
|
|||||||
easing='easeOutQuint'
|
easing='easeOutQuint'
|
||||||
)
|
)
|
||||||
v-flex(xs12 md6 lg4 xl3 d-flex)
|
v-flex(xs12 md6 lg4 xl3 d-flex)
|
||||||
v-card.indigo.lighten-2.dashboard-card.animated.fadeInUp.wait-p4s(dark)
|
v-card.indigo.lighten-1.dashboard-card.animated.fadeInUp.wait-p4s(dark)
|
||||||
v-card-text
|
v-card-text
|
||||||
v-icon.dashboard-icon mdi-account-group
|
v-icon.dashboard-icon mdi-account-group
|
||||||
.overline {{$t('admin:dashboard.groups')}}
|
.overline {{$t('admin:dashboard.groups')}}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
persistent-hint
|
persistent-hint
|
||||||
label='LDAP Debug'
|
label='LDAP Debug'
|
||||||
v-model='flags.ldapdebug'
|
v-model='flags.ldapdebug'
|
||||||
|
inset
|
||||||
)
|
)
|
||||||
v-divider.mt-3
|
v-divider.mt-3
|
||||||
v-switch.mt-3(
|
v-switch.mt-3(
|
||||||
@ -31,6 +32,7 @@
|
|||||||
persistent-hint
|
persistent-hint
|
||||||
label='SQL Query Logging'
|
label='SQL Query Logging'
|
||||||
v-model='flags.sqllog'
|
v-model='flags.sqllog'
|
||||||
|
inset
|
||||||
)
|
)
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -98,6 +98,7 @@
|
|||||||
v-chip(label, color='white', small).indigo--text coming soon
|
v-chip(label, color='white', small).indigo--text coming soon
|
||||||
v-card-text
|
v-card-text
|
||||||
v-switch(
|
v-switch(
|
||||||
|
inset
|
||||||
label='Asset Image Optimization'
|
label='Asset Image Optimization'
|
||||||
color='indigo'
|
color='indigo'
|
||||||
v-model='config.featureTinyPNG'
|
v-model='config.featureTinyPNG'
|
||||||
@ -118,6 +119,7 @@
|
|||||||
|
|
||||||
v-divider.mt-3
|
v-divider.mt-3
|
||||||
v-switch(
|
v-switch(
|
||||||
|
inset
|
||||||
label='Page Ratings'
|
label='Page Ratings'
|
||||||
color='indigo'
|
color='indigo'
|
||||||
v-model='config.featurePageRatings'
|
v-model='config.featurePageRatings'
|
||||||
@ -128,6 +130,7 @@
|
|||||||
|
|
||||||
v-divider.mt-3
|
v-divider.mt-3
|
||||||
v-switch(
|
v-switch(
|
||||||
|
inset
|
||||||
label='Page Comments'
|
label='Page Comments'
|
||||||
color='indigo'
|
color='indigo'
|
||||||
v-model='config.featurePageComments'
|
v-model='config.featurePageComments'
|
||||||
@ -138,6 +141,7 @@
|
|||||||
|
|
||||||
v-divider.mt-3
|
v-divider.mt-3
|
||||||
v-switch(
|
v-switch(
|
||||||
|
inset
|
||||||
label='Personal Wikis'
|
label='Personal Wikis'
|
||||||
color='indigo'
|
color='indigo'
|
||||||
v-model='config.featurePersonalWikis'
|
v-model='config.featurePersonalWikis'
|
||||||
@ -152,6 +156,7 @@
|
|||||||
v-card-text
|
v-card-text
|
||||||
v-alert(outlined, color='red darken-2', icon='mdi-information-outline').body-2 Make sure to understand the implications before turning on / off a security feature.
|
v-alert(outlined, color='red darken-2', icon='mdi-information-outline').body-2 Make sure to understand the implications before turning on / off a security feature.
|
||||||
v-switch.mt-3(
|
v-switch.mt-3(
|
||||||
|
inset
|
||||||
label='Block IFrame Embedding'
|
label='Block IFrame Embedding'
|
||||||
color='red darken-2'
|
color='red darken-2'
|
||||||
v-model='config.securityIframe'
|
v-model='config.securityIframe'
|
||||||
@ -160,6 +165,7 @@
|
|||||||
)
|
)
|
||||||
v-divider.mt-3
|
v-divider.mt-3
|
||||||
v-switch(
|
v-switch(
|
||||||
|
inset
|
||||||
label='Same Origin Referrer Policy'
|
label='Same Origin Referrer Policy'
|
||||||
color='red darken-2'
|
color='red darken-2'
|
||||||
v-model='config.securityReferrerPolicy'
|
v-model='config.securityReferrerPolicy'
|
||||||
@ -169,6 +175,7 @@
|
|||||||
|
|
||||||
v-divider.mt-3
|
v-divider.mt-3
|
||||||
v-switch(
|
v-switch(
|
||||||
|
inset
|
||||||
label='Enforce HSTS'
|
label='Enforce HSTS'
|
||||||
color='red darken-2'
|
color='red darken-2'
|
||||||
v-model='config.securityHSTS'
|
v-model='config.securityHSTS'
|
||||||
@ -191,6 +198,7 @@
|
|||||||
|
|
||||||
v-divider.mt-3
|
v-divider.mt-3
|
||||||
v-switch(
|
v-switch(
|
||||||
|
inset
|
||||||
label='Enforce CSP'
|
label='Enforce CSP'
|
||||||
color='red darken-2'
|
color='red darken-2'
|
||||||
v-model='config.securityCSP'
|
v-model='config.securityCSP'
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
v-list-item-subtitle(v-html='data.item.nativeName')
|
v-list-item-subtitle(v-html='data.item.nativeName')
|
||||||
v-divider.mt-3
|
v-divider.mt-3
|
||||||
v-switch(
|
v-switch(
|
||||||
|
inset
|
||||||
v-model='autoUpdate'
|
v-model='autoUpdate'
|
||||||
:label='$t("admin:locale.autoUpdate.label")'
|
:label='$t("admin:locale.autoUpdate.label")'
|
||||||
color='primary'
|
color='primary'
|
||||||
@ -52,6 +53,7 @@
|
|||||||
v-toolbar-title.subtitle-1 {{ $t('admin:locale.namespacing') }}
|
v-toolbar-title.subtitle-1 {{ $t('admin:locale.namespacing') }}
|
||||||
v-card-text
|
v-card-text
|
||||||
v-switch(
|
v-switch(
|
||||||
|
inset
|
||||||
v-model='namespacing'
|
v-model='namespacing'
|
||||||
:label='$t("admin:locale.namespaces.label")'
|
:label='$t("admin:locale.namespaces.label")'
|
||||||
color='primary'
|
color='primary'
|
||||||
|
@ -64,6 +64,7 @@
|
|||||||
persistent-hint
|
persistent-hint
|
||||||
:hint='$t(`admin:mail.smtpTLSHint`)'
|
:hint='$t(`admin:mail.smtpTLSHint`)'
|
||||||
prepend-icon='mdi-security-network'
|
prepend-icon='mdi-security-network'
|
||||||
|
inset
|
||||||
)
|
)
|
||||||
v-text-field.mt-3(
|
v-text-field.mt-3(
|
||||||
outlined
|
outlined
|
||||||
@ -94,6 +95,7 @@
|
|||||||
:label='$t(`admin:mail.dkimUse`)'
|
:label='$t(`admin:mail.dkimUse`)'
|
||||||
color='primary'
|
color='primary'
|
||||||
prepend-icon='mdi-key'
|
prepend-icon='mdi-key'
|
||||||
|
inset
|
||||||
)
|
)
|
||||||
v-text-field(
|
v-text-field(
|
||||||
outlined
|
outlined
|
||||||
|
@ -82,6 +82,7 @@
|
|||||||
label='Enabled'
|
label='Enabled'
|
||||||
v-model='currentRenderer.isEnabled'
|
v-model='currentRenderer.isEnabled'
|
||||||
hide-details
|
hide-details
|
||||||
|
inset
|
||||||
)
|
)
|
||||||
v-card-text.pb-4.pt-2.pl-4
|
v-card-text.pb-4.pt-2.pl-4
|
||||||
.overline.my-5 Rendering Module Configuration
|
.overline.my-5 Rendering Module Configuration
|
||||||
@ -106,6 +107,7 @@
|
|||||||
color='primary'
|
color='primary'
|
||||||
:hint='cfg.value.hint ? cfg.value.hint : ""'
|
:hint='cfg.value.hint ? cfg.value.hint : ""'
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
inset
|
||||||
)
|
)
|
||||||
v-text-field(
|
v-text-field(
|
||||||
v-else
|
v-else
|
||||||
|
@ -69,6 +69,7 @@
|
|||||||
prepend-icon='mdi-settings-box'
|
prepend-icon='mdi-settings-box'
|
||||||
:hint='cfg.value.hint ? cfg.value.hint : ""'
|
:hint='cfg.value.hint ? cfg.value.hint : ""'
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
inset
|
||||||
)
|
)
|
||||||
v-textarea(
|
v-textarea(
|
||||||
v-else-if='cfg.value.type === "string" && cfg.value.multiline'
|
v-else-if='cfg.value.type === "string" && cfg.value.multiline'
|
||||||
|
@ -80,6 +80,15 @@
|
|||||||
v-card.wiki-form.animated.fadeInUp.wait-p2s
|
v-card.wiki-form.animated.fadeInUp.wait-p2s
|
||||||
v-toolbar(color='primary', dense, flat, dark)
|
v-toolbar(color='primary', dense, flat, dark)
|
||||||
.subtitle-1 {{target.title}}
|
.subtitle-1 {{target.title}}
|
||||||
|
v-spacer
|
||||||
|
v-switch(
|
||||||
|
dark
|
||||||
|
color='blue lighten-5'
|
||||||
|
label='Active'
|
||||||
|
v-model='target.isEnabled'
|
||||||
|
hide-details
|
||||||
|
inset
|
||||||
|
)
|
||||||
v-card-text
|
v-card-text
|
||||||
v-form
|
v-form
|
||||||
.targetlogo
|
.targetlogo
|
||||||
@ -115,6 +124,7 @@
|
|||||||
prepend-icon='mdi-settings-box'
|
prepend-icon='mdi-settings-box'
|
||||||
:hint='cfg.value.hint ? cfg.value.hint : ""'
|
:hint='cfg.value.hint ? cfg.value.hint : ""'
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
inset
|
||||||
)
|
)
|
||||||
v-textarea(
|
v-textarea(
|
||||||
v-else-if='cfg.value.type === "string" && cfg.value.multiline'
|
v-else-if='cfg.value.type === "string" && cfg.value.multiline'
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
)
|
)
|
||||||
v-divider.mt-3
|
v-divider.mt-3
|
||||||
v-switch(
|
v-switch(
|
||||||
|
inset
|
||||||
v-model='darkMode'
|
v-model='darkMode'
|
||||||
:label='$t(`admin:theme.darkMode`)'
|
:label='$t(`admin:theme.darkMode`)'
|
||||||
color='primary'
|
color='primary'
|
||||||
|
@ -125,6 +125,11 @@
|
|||||||
//- v-btn(depressed, color='grey darken-3', block)
|
//- v-btn(depressed, color='grey darken-3', block)
|
||||||
//- v-icon(left) mdi-cached
|
//- v-icon(left) mdi-cached
|
||||||
//- span Reset
|
//- span Reset
|
||||||
|
v-tooltip(bottom, v-if='isAuthenticated && isAdmin')
|
||||||
|
template(v-slot:activator='{ on }')
|
||||||
|
v-btn.ml-2.mr-0(icon, v-on='on', href='/t')
|
||||||
|
v-icon(color='grey') mdi-tag-multiple
|
||||||
|
span Browse Tags
|
||||||
v-flex(xs6, md4)
|
v-flex(xs6, md4)
|
||||||
v-toolbar.nav-header-inner.pr-4(color='black', dark, flat)
|
v-toolbar.nav-header-inner.pr-4(color='black', dark, flat)
|
||||||
v-spacer
|
v-spacer
|
||||||
|
@ -90,6 +90,7 @@
|
|||||||
:hint='$t(`editor:props.publishToggleHint`)'
|
:hint='$t(`editor:props.publishToggleHint`)'
|
||||||
persistent-hint
|
persistent-hint
|
||||||
disabled
|
disabled
|
||||||
|
inset
|
||||||
)
|
)
|
||||||
v-divider
|
v-divider
|
||||||
v-card-text.grey.pt-5(:class='darkMode ? `darken-3-d3` : `lighten-5`')
|
v-card-text.grey.pt-5(:class='darkMode ? `darken-3-d3` : `lighten-5`')
|
||||||
@ -190,6 +191,7 @@
|
|||||||
:hint='$t(`editor:props.allowCommentsHint`)'
|
:hint='$t(`editor:props.allowCommentsHint`)'
|
||||||
persistent-hint
|
persistent-hint
|
||||||
disabled
|
disabled
|
||||||
|
inset
|
||||||
)
|
)
|
||||||
v-switch(
|
v-switch(
|
||||||
:label='$t(`editor:props.allowRatings`)'
|
:label='$t(`editor:props.allowRatings`)'
|
||||||
@ -198,6 +200,7 @@
|
|||||||
:hint='$t(`editor:props.allowRatingsHint`)'
|
:hint='$t(`editor:props.allowRatingsHint`)'
|
||||||
persistent-hint
|
persistent-hint
|
||||||
disabled
|
disabled
|
||||||
|
inset
|
||||||
)
|
)
|
||||||
v-switch(
|
v-switch(
|
||||||
:label='$t(`editor:props.displayAuthor`)'
|
:label='$t(`editor:props.displayAuthor`)'
|
||||||
@ -206,6 +209,7 @@
|
|||||||
:hint='$t(`editor:props.displayAuthorHint`)'
|
:hint='$t(`editor:props.displayAuthorHint`)'
|
||||||
persistent-hint
|
persistent-hint
|
||||||
disabled
|
disabled
|
||||||
|
inset
|
||||||
)
|
)
|
||||||
v-switch(
|
v-switch(
|
||||||
:label='$t(`editor:props.displaySharingBar`)'
|
:label='$t(`editor:props.displaySharingBar`)'
|
||||||
@ -214,6 +218,7 @@
|
|||||||
:hint='$t(`editor:props.displaySharingBarHint`)'
|
:hint='$t(`editor:props.displaySharingBarHint`)'
|
||||||
persistent-hint
|
persistent-hint
|
||||||
disabled
|
disabled
|
||||||
|
inset
|
||||||
)
|
)
|
||||||
|
|
||||||
page-selector(mode='create', v-model='pageSelectorShown', :path='path', :locale='locale', :open-handler='setPath')
|
page-selector(mode='create', v-model='pageSelectorShown', :path='path', :locale='locale', :open-handler='setPath')
|
||||||
|
167
client/components/tags.vue
Normal file
167
client/components/tags.vue
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
<template lang='pug'>
|
||||||
|
v-app(:dark='darkMode').tags
|
||||||
|
nav-header
|
||||||
|
v-navigation-drawer.pb-0.elevation-1(app, fixed, clipped, :right='$vuetify.rtl', permanent, width='300')
|
||||||
|
vue-scroll(:ops='scrollStyle')
|
||||||
|
v-list(dense, nav)
|
||||||
|
v-list-item(href='/')
|
||||||
|
v-list-item-icon: v-icon mdi-home
|
||||||
|
v-list-item-title {{$t('common:header.home')}}
|
||||||
|
template(v-for='(tags, groupName) in tagsGrouped')
|
||||||
|
v-divider.my-2
|
||||||
|
v-subheader.pl-4(:key='`tagGroup-` + groupName') {{groupName}}
|
||||||
|
v-list-item(v-for='tag of tags', @click='toggleTag(tag.tag)', :key='`tag-` + tag.tag')
|
||||||
|
v-list-item-icon
|
||||||
|
v-icon(v-if='isSelected(tag.tag)', color='primary') mdi-checkbox-intermediate
|
||||||
|
v-icon(v-else) mdi-checkbox-blank-outline
|
||||||
|
v-list-item-title {{tag.title}}
|
||||||
|
v-content
|
||||||
|
v-toolbar(color='primary', dark, flat, height='58')
|
||||||
|
template(v-if='selection.length > 0')
|
||||||
|
.overline.mr-3.animated.fadeInLeft Current Selection
|
||||||
|
v-chip.mr-3.primary--text(
|
||||||
|
v-for='tag of tagsSelected'
|
||||||
|
color='white'
|
||||||
|
close
|
||||||
|
@click:close='toggleTag(tag.tag)'
|
||||||
|
) {{tag.title}}
|
||||||
|
v-spacer
|
||||||
|
v-btn.animated.fadeIn(
|
||||||
|
small
|
||||||
|
outlined
|
||||||
|
color='blue lighten-4'
|
||||||
|
rounded
|
||||||
|
@click='selection = []'
|
||||||
|
)
|
||||||
|
v-icon(left) mdi-close
|
||||||
|
span Clear Selection
|
||||||
|
template(v-else)
|
||||||
|
v-icon.mr-3.animated.fadeInRight mdi-arrow-left
|
||||||
|
.overline.animated.fadeInRight Select one or more tags
|
||||||
|
v-toolbar(color='grey lighten-4', flat, height='58')
|
||||||
|
v-text-field.tags-search(
|
||||||
|
label='Search within results...'
|
||||||
|
solo
|
||||||
|
hide-details
|
||||||
|
flat
|
||||||
|
rounded
|
||||||
|
single-line
|
||||||
|
height='40'
|
||||||
|
prepend-icon='mdi-file-document-box-search-outline'
|
||||||
|
append-icon='mdi-arrow-right'
|
||||||
|
)
|
||||||
|
v-divider.mx-3(vertical)
|
||||||
|
.overline Order By
|
||||||
|
v-select.ml-2(
|
||||||
|
:items='orderByItems'
|
||||||
|
v-model='orderBy'
|
||||||
|
background-color='white'
|
||||||
|
hide-details
|
||||||
|
label='Order By'
|
||||||
|
rounded
|
||||||
|
single-line
|
||||||
|
dense
|
||||||
|
height='40'
|
||||||
|
style='max-width: 250px;'
|
||||||
|
)
|
||||||
|
v-divider.mx-3(vertical)
|
||||||
|
v-btn-toggle(v-model='displayStyle', rounded, mandatory)
|
||||||
|
v-btn(text, height='40'): v-icon(small) mdi-view-list
|
||||||
|
v-btn(text, height='40'): v-icon(small) mdi-cards-variant
|
||||||
|
v-btn(text, height='40'): v-icon(small) mdi-format-align-justify
|
||||||
|
v-divider
|
||||||
|
nav-footer
|
||||||
|
notify
|
||||||
|
search-results
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { get } from 'vuex-pathify'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
|
import tagsQuery from 'gql/common/common-pages-query-tags.gql'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tags: [],
|
||||||
|
selection: [],
|
||||||
|
displayStyle: 0,
|
||||||
|
orderBy: 'TITLE',
|
||||||
|
orderByItems: [
|
||||||
|
{ text: 'Creation Date', value: 'CREATED' },
|
||||||
|
{ text: 'ID', value: 'ID' },
|
||||||
|
{ text: 'Last Modified', value: 'UPDATED' },
|
||||||
|
{ text: 'Path', value: 'PATH' },
|
||||||
|
{ text: 'Title', value: 'TITLE' }
|
||||||
|
],
|
||||||
|
scrollStyle: {
|
||||||
|
vuescroll: {},
|
||||||
|
scrollPanel: {
|
||||||
|
initialScrollY: 0,
|
||||||
|
initialScrollX: 0,
|
||||||
|
scrollingX: false,
|
||||||
|
easing: 'easeOutQuad',
|
||||||
|
speed: 1000,
|
||||||
|
verticalNativeBarPos: this.$vuetify.rtl ? `left` : `right`
|
||||||
|
},
|
||||||
|
rail: {
|
||||||
|
gutterOfEnds: '2px'
|
||||||
|
},
|
||||||
|
bar: {
|
||||||
|
onlyShowBarOnScroll: false,
|
||||||
|
background: '#CCC',
|
||||||
|
hoverStyle: {
|
||||||
|
background: '#999'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
darkMode: get('site/dark'),
|
||||||
|
tagsGrouped () {
|
||||||
|
return _.groupBy(this.tags, t => t.title.charAt(0).toUpperCase())
|
||||||
|
},
|
||||||
|
tagsSelected () {
|
||||||
|
return _.filter(this.tags, t => _.includes(this.selection, t.tag))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.$store.commit('page/SET_MODE', 'tags')
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleTag (tag) {
|
||||||
|
if (_.includes(this.selection, tag)) {
|
||||||
|
this.selection = _.without(this.selection, tag)
|
||||||
|
} else {
|
||||||
|
this.selection.push(tag)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isSelected (tag) {
|
||||||
|
return _.includes(this.selection, tag)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
apollo: {
|
||||||
|
tags: {
|
||||||
|
query: tagsQuery,
|
||||||
|
fetchPolicy: 'cache-and-network',
|
||||||
|
update: (data) => _.cloneDeep(data.pages.tags),
|
||||||
|
watchLoading (isLoading) {
|
||||||
|
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'tags-refresh')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang='scss'>
|
||||||
|
.tags-search {
|
||||||
|
.v-input__control {
|
||||||
|
min-height: initial !important;
|
||||||
|
}
|
||||||
|
.v-input__prepend-outer {
|
||||||
|
margin-top: 8px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
8
client/graph/common/common-pages-query-tags.gql
Normal file
8
client/graph/common/common-pages-query-tags.gql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
query {
|
||||||
|
pages {
|
||||||
|
tags {
|
||||||
|
tag
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -165,6 +165,14 @@ router.get(['/s', '/s/*'], async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tags
|
||||||
|
*/
|
||||||
|
router.get(['/t', '/t/*'], (req, res, next) => {
|
||||||
|
_.set(res.locals, 'pageMeta.title', 'Tags')
|
||||||
|
res.render('tags')
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View document / asset
|
* View document / asset
|
||||||
*/
|
*/
|
||||||
|
@ -76,6 +76,9 @@ module.exports = {
|
|||||||
} else {
|
} else {
|
||||||
throw new WIKI.Error.PageNotFound()
|
throw new WIKI.Error.PageNotFound()
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
async tags (obj, args, context, info) {
|
||||||
|
return WIKI.models.tags.query().orderBy('tag', 'asc')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
PageMutation: {
|
PageMutation: {
|
||||||
|
@ -36,6 +36,8 @@ type PageQuery {
|
|||||||
single(
|
single(
|
||||||
id: Int!
|
id: Int!
|
||||||
): Page @auth(requires: ["manage:pages", "delete:pages", "manage:system"])
|
): Page @auth(requires: ["manage:pages", "delete:pages", "manage:system"])
|
||||||
|
|
||||||
|
tags: [PageTag]! @auth(requires: ["manage:system", "read:pages"])
|
||||||
}
|
}
|
||||||
|
|
||||||
# -----------------------------------------------
|
# -----------------------------------------------
|
||||||
@ -109,6 +111,7 @@ type Page {
|
|||||||
privateNS: String
|
privateNS: String
|
||||||
publishStartDate: Date!
|
publishStartDate: Date!
|
||||||
publishEndDate: String!
|
publishEndDate: String!
|
||||||
|
tags: [PageTag]!
|
||||||
content: String!
|
content: String!
|
||||||
render: String
|
render: String
|
||||||
toc: String
|
toc: String
|
||||||
@ -125,6 +128,14 @@ type Page {
|
|||||||
creatorEmail: String!
|
creatorEmail: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PageTag {
|
||||||
|
id: Int!
|
||||||
|
tag: String!
|
||||||
|
title: String
|
||||||
|
createdAt: Date!
|
||||||
|
updatedAt: Date!
|
||||||
|
}
|
||||||
|
|
||||||
type PageHistory {
|
type PageHistory {
|
||||||
versionId: Int!
|
versionId: Int!
|
||||||
authorId: Int!
|
authorId: Int!
|
||||||
|
@ -210,6 +210,11 @@ module.exports = class Page extends Model {
|
|||||||
isPrivate: opts.isPrivate
|
isPrivate: opts.isPrivate
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// -> Save Tags
|
||||||
|
if (opts.tags.length > 0) {
|
||||||
|
await WIKI.models.tags.associateTags({ tags: opts.tags, page })
|
||||||
|
}
|
||||||
|
|
||||||
// -> Render page to HTML
|
// -> Render page to HTML
|
||||||
await WIKI.models.pages.renderPage(page)
|
await WIKI.models.pages.renderPage(page)
|
||||||
|
|
||||||
@ -260,6 +265,9 @@ module.exports = class Page extends Model {
|
|||||||
isPrivate: ogPage.isPrivate
|
isPrivate: ogPage.isPrivate
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// -> Save Tags
|
||||||
|
await WIKI.models.tags.associateTags({ tags: opts.tags, page })
|
||||||
|
|
||||||
// -> Render page to HTML
|
// -> Render page to HTML
|
||||||
await WIKI.models.pages.renderPage(page)
|
await WIKI.models.pages.renderPage(page)
|
||||||
|
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
const Model = require('objection').Model
|
const Model = require('objection').Model
|
||||||
|
const _ = require('lodash')
|
||||||
|
|
||||||
|
/* global WIKI */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tags model
|
* Tags model
|
||||||
@ -46,4 +49,51 @@ module.exports = class Tag extends Model {
|
|||||||
this.createdAt = new Date().toISOString()
|
this.createdAt = new Date().toISOString()
|
||||||
this.updatedAt = new Date().toISOString()
|
this.updatedAt = new Date().toISOString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async associateTags ({ tags, page }) {
|
||||||
|
let existingTags = await WIKI.models.tags.query().column('id', 'tag')
|
||||||
|
|
||||||
|
// Create missing tags
|
||||||
|
|
||||||
|
const newTags = _.filter(tags, t => !_.some(existingTags, ['tag', t])).map(t => ({
|
||||||
|
tag: t,
|
||||||
|
title: t
|
||||||
|
}))
|
||||||
|
if (newTags.length > 0) {
|
||||||
|
if (WIKI.config.db.type === 'postgres') {
|
||||||
|
const createdTags = await WIKI.models.tags.query().insert(newTags)
|
||||||
|
existingTags = _.concat(existingTags, createdTags)
|
||||||
|
} else {
|
||||||
|
for (const newTag of newTags) {
|
||||||
|
const createdTag = await WIKI.models.tags.query().insert(newTag)
|
||||||
|
existingTags.push(createdTag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch current page tags
|
||||||
|
|
||||||
|
const targetTags = _.filter(existingTags, t => _.includes(tags, t.tag))
|
||||||
|
const currentTags = await page.$relatedQuery('tags')
|
||||||
|
|
||||||
|
// Tags to relate
|
||||||
|
|
||||||
|
const tagsToRelate = _.differenceBy(targetTags, currentTags, 'id')
|
||||||
|
if (tagsToRelate.length > 0) {
|
||||||
|
if (WIKI.config.db.type === 'postgres') {
|
||||||
|
await page.$relatedQuery('tags').relate(tagsToRelate)
|
||||||
|
} else {
|
||||||
|
for (const tag of tagsToRelate) {
|
||||||
|
await page.$relatedQuery('tags').relate(tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tags to unrelate
|
||||||
|
|
||||||
|
const tagsToUnrelate = _.differenceBy(currentTags, targetTags, 'id')
|
||||||
|
if (tagsToUnrelate.length > 0) {
|
||||||
|
await page.$relatedQuery('tags').unrelate().whereIn('tags.id', _.map(tagsToUnrelate, 'id'))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
5
server/views/tags.pug
Normal file
5
server/views/tags.pug
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
extends master.pug
|
||||||
|
|
||||||
|
block body
|
||||||
|
#root
|
||||||
|
tags
|
Loading…
Reference in New Issue
Block a user