diff --git a/.gitignore b/.gitignore
index 41183d2b..2db728f8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,9 +19,7 @@ npm-debug.log*
# Generated assets
/assets
/assets-legacy
-server/views/master.pug
-server/views/legacy/master.pug
-server/views/setup.pug
+server/views/base.pug
# Webpack
.webpack-cache
diff --git a/client/client-app.js b/client/client-app.js
index 6d4c4340..213e509a 100644
--- a/client/client-app.js
+++ b/client/client-app.js
@@ -134,27 +134,22 @@ Vue.prototype.Velocity = Velocity
// Register Vue Components
// ====================================
-Vue.component('Admin', () => import(/* webpackChunkName: "admin" */ './components/admin.vue'))
Vue.component('Comments', () => import(/* webpackChunkName: "comments" */ './components/comments.vue'))
Vue.component('Editor', () => import(/* webpackPrefetch: -100, webpackChunkName: "editor" */ './components/editor.vue'))
Vue.component('History', () => import(/* webpackChunkName: "history" */ './components/history.vue'))
Vue.component('Loader', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/loader.vue'))
-Vue.component('Login', () => import(/* webpackPrefetch: true, webpackChunkName: "login" */ './components/login.vue'))
Vue.component('NavHeader', () => import(/* webpackMode: "eager" */ './components/common/nav-header.vue'))
Vue.component('NewPage', () => import(/* webpackChunkName: "new-page" */ './components/new-page.vue'))
Vue.component('Notify', () => import(/* webpackMode: "eager" */ './components/common/notify.vue'))
Vue.component('NotFound', () => import(/* webpackChunkName: "not-found" */ './components/not-found.vue'))
Vue.component('PageSelector', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/page-selector.vue'))
Vue.component('PageSource', () => import(/* webpackChunkName: "source" */ './components/source.vue'))
-Vue.component('Profile', () => import(/* webpackChunkName: "profile" */ './components/profile.vue'))
-Vue.component('Register', () => import(/* webpackChunkName: "register" */ './components/register.vue'))
Vue.component('SearchResults', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/search-results.vue'))
Vue.component('SocialSharing', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/social-sharing.vue'))
Vue.component('Tags', () => import(/* webpackChunkName: "tags" */ './components/tags.vue'))
Vue.component('Unauthorized', () => import(/* webpackChunkName: "unauthorized" */ './components/unauthorized.vue'))
Vue.component('VCardChin', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/v-card-chin.vue'))
Vue.component('VCardInfo', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/v-card-info.vue'))
-Vue.component('Welcome', () => import(/* webpackChunkName: "welcome" */ './components/welcome.vue'))
Vue.component('NavFooter', () => import(/* webpackChunkName: "theme" */ './themes/' + siteConfig.theme + '/components/nav-footer.vue'))
Vue.component('Page', () => import(/* webpackChunkName: "theme" */ './themes/' + siteConfig.theme + '/components/page.vue'))
diff --git a/client/components/admin.vue b/client/components/admin.vue
deleted file mode 100644
index f6e3722a..00000000
--- a/client/components/admin.vue
+++ /dev/null
@@ -1,335 +0,0 @@
-
- v-app.admin
- nav-header(hide-search)
- template(slot='mid')
- v-spacer
- .overline.grey--text {{$t('admin:adminArea')}}
- v-spacer
- v-navigation-drawer.pb-0.admin-sidebar(v-model='adminDrawerShown', app, fixed, clipped, :right='$vuetify.rtl', permanent, width='300', :class='$vuetify.theme.dark ? `grey darken-4` : ``')
- vue-scroll(:ops='scrollStyle')
- v-list.radius-0(dense, nav)
- v-list-item(to='/dashboard', color='primary')
- v-list-item-avatar(size='24', tile): 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', color='primary', v-if='hasPermission(`manage:system`)')
- v-list-item-avatar(size='24', tile): v-icon mdi-widgets
- v-list-item-title {{ $t('admin:general.title') }}
- v-list-item(to='/locale', color='primary', v-if='hasPermission(`manage:system`)')
- v-list-item-avatar(size='24', tile): v-icon mdi-web
- v-list-item-title {{ $t('admin:locale.title') }}
- v-list-item(to='/navigation', color='primary', v-if='hasPermission([`manage:system`, `manage:navigation`])')
- v-list-item-avatar(size='24', tile): v-icon mdi-near-me
- v-list-item-title {{ $t('admin:navigation.title') }}
- v-list-item(to='/pages', color='primary', v-if='hasPermission([`manage:system`, `write:pages`, `manage:pages`, `delete:pages`])')
- v-list-item-avatar(size='24', tile): v-icon mdi-file-document-outline
- v-list-item-title {{ $t('admin:pages.title') }}
- v-list-item-action(style='min-width:auto;')
- v-chip(x-small, :color='$vuetify.theme.dark ? `grey darken-3-d4` : `grey lighten-5`')
- .caption.grey--text {{ info.pagesTotal }}
- v-list-item(to='/tags', v-if='hasPermission([`manage:system`])')
- v-list-item-avatar(size='24', tile): v-icon mdi-tag-multiple
- v-list-item-title {{ $t('admin:tags.title') }}
- v-list-item-action(style='min-width:auto;')
- v-chip(x-small, :color='$vuetify.theme.dark ? `grey darken-3-d4` : `grey lighten-5`')
- .caption.grey--text {{ info.tagsTotal }}
- v-list-item(to='/theme', color='primary', v-if='hasPermission([`manage:system`, `manage:theme`])')
- v-list-item-avatar(size='24', tile): 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', color='primary', v-if='hasPermission([`manage:system`, `manage:groups`, `write:groups`])')
- v-list-item-avatar(size='24', tile): 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='$vuetify.theme.dark ? `grey darken-3-d4` : `grey lighten-4`')
- .caption.grey--text {{ info.groupsTotal }}
- 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', tile): v-icon mdi-account-box
- v-list-item-title {{ $t('admin:users.title') }}
- v-list-item-action(style='min-width:auto;')
- v-chip(x-small, :color='$vuetify.theme.dark ? `grey darken-3-d4` : `grey lighten-4`')
- .caption.grey--text {{ info.usersTotal }}
- template(v-if='hasPermission(`manage:system`)')
- v-divider.my-2
- v-subheader.pl-4 {{ $t('admin:nav.modules') }}
- v-list-item(to='/analytics', color='primary')
- v-list-item-avatar(size='24', tile): v-icon mdi-chart-timeline-variant
- v-list-item-title {{ $t('admin:analytics.title') }}
- v-list-item(to='/auth', color='primary')
- v-list-item-avatar(size='24', tile): v-icon mdi-lock-outline
- v-list-item-title {{ $t('admin:auth.title') }}
- v-list-item(to='/comments')
- v-list-item-avatar(size='24', tile): v-icon mdi-comment-text-outline
- v-list-item-title {{ $t('admin:comments.title') }}
- v-list-item(to='/editor', disabled)
- v-list-item-avatar(size='24', tile): v-icon(color='grey lighten-2') mdi-playlist-edit
- v-list-item-title {{ $t('admin:editor.title') }}
- v-list-item(to='/extensions')
- v-list-item-avatar(size='24', tile): v-icon mdi-chip
- v-list-item-title {{ $t('admin:extensions.title') }}
- v-list-item(to='/logging', disabled)
- v-list-item-avatar(size='24', tile): v-icon(color='grey lighten-2') mdi-script-text-outline
- v-list-item-title {{ $t('admin:logging.title') }}
- v-list-item(to='/rendering', color='primary')
- v-list-item-avatar(size='24', tile): v-icon mdi-cogs
- v-list-item-title {{ $t('admin:rendering.title') }}
- v-list-item(to='/search', color='primary')
- v-list-item-avatar(size='24', tile): v-icon mdi-cloud-search-outline
- v-list-item-title {{ $t('admin:search.title') }}
- v-list-item(to='/storage', color='primary')
- v-list-item-avatar(size='24', tile): v-icon mdi-harddisk
- v-list-item-title {{ $t('admin:storage.title') }}
- template(v-if='hasPermission([`manage:system`, `manage:api`])')
- v-divider.my-2
- v-subheader.pl-4 {{ $t('admin:nav.system') }}
- v-list-item(to='/api', v-if='hasPermission([`manage:system`, `manage:api`])')
- v-list-item-avatar(size='24', tile): v-icon mdi-call-split
- v-list-item-title {{ $t('admin:api.title') }}
- v-list-item(to='/mail', color='primary', v-if='hasPermission(`manage:system`)')
- v-list-item-avatar(size='24', tile): v-icon mdi-email-multiple-outline
- v-list-item-title {{ $t('admin:mail.title') }}
- v-list-item(to='/security', v-if='hasPermission(`manage:system`)')
- v-list-item-avatar(size='24', tile): v-icon mdi-lock-check
- v-list-item-title {{ $t('admin:security.title') }}
- v-list-item(to='/ssl', v-if='hasPermission(`manage:system`)')
- v-list-item-avatar(size='24', tile): 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', tile): v-icon mdi-tune
- v-list-item-title {{ $t('admin:system.title') }}
- v-list-item(to='/utilities', color='primary', v-if='hasPermission(`manage:system`)')
- v-list-item-avatar(size='24', tile): v-icon mdi-wrench-outline
- v-list-item-title {{ $t('admin:utilities.title') }}
- v-list-item(to='/webhooks', v-if='hasPermission(`manage:system`)', disabled)
- v-list-item-avatar(size='24', tile): v-icon(color='grey lighten-2') mdi-webhook
- v-list-item-title {{ $t('admin:webhooks.title') }}
- v-list-group(
- to='/dev'
- no-action
- v-if='hasPermission([`manage:system`, `manage:api`])'
- )
- v-list-item(slot='activator')
- v-list-item-avatar(size='24', tile): v-icon mdi-dev-to
- v-list-item-title {{ $t('admin:dev.title') }}
-
- v-list-item(to='/dev-flags', color='primary')
- v-list-item-title {{ $t('admin:dev.flags.title') }}
- 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', color='primary')
- v-list-item-avatar(size='24', tile): v-icon mdi-heart-outline
- v-list-item-title {{ $t('admin:contribute.title') }}
-
- v-main(:class='$vuetify.theme.dark ? "grey darken-5" : "grey lighten-5"')
- transition(name='admin-router')
- router-view
-
- nav-footer
- notify
- search-results
-
-
-
-
-
diff --git a/client/components/admin/admin-analytics.vue b/client/components/admin/admin-analytics.vue
deleted file mode 100644
index dd760502..00000000
--- a/client/components/admin/admin-analytics.vue
+++ /dev/null
@@ -1,181 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row, wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-line-chart.svg', alt='Analytics', style='width: 80px;')
- .admin-header-title
- .headline.primary--text.animated.fadeInLeft {{ $t('admin:analytics.title') }}
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p4s {{ $t('admin:analytics.subtitle') }}
- v-spacer
- v-btn.animated.fadeInDown.wait-p2s.mr-3(icon, outlined, color='grey', @click='refresh')
- v-icon mdi-refresh
- v-btn.animated.fadeInDown(color='success', @click='save', depressed, large)
- v-icon(left) mdi-check
- span {{$t('common:actions.apply')}}
-
- v-flex(lg3, xs12)
- v-card.animated.fadeInUp
- v-toolbar(flat, color='primary', dark, dense)
- .subtitle-1 {{$t('admin:analytics.providers')}}
- v-list(two-line, dense).py-0
- template(v-for='(str, idx) in providers')
- v-list-item(:key='str.key', @click='selectedProvider = str.key', :disabled='!str.isAvailable')
- v-list-item-avatar(size='24')
- v-icon(color='grey', v-if='!str.isAvailable') mdi-minus-box-outline
- v-icon(color='primary', v-else-if='str.isEnabled', v-ripple, @click='str.isEnabled = false') mdi-checkbox-marked-outline
- v-icon(color='grey', v-else, v-ripple, @click='str.isEnabled = true') mdi-checkbox-blank-outline
- v-list-item-content
- v-list-item-title.body-2(:class='!str.isAvailable ? `grey--text` : (selectedProvider === str.key ? `primary--text` : ``)') {{ str.title }}
- v-list-item-subtitle: .caption(:class='!str.isAvailable ? `grey--text text--lighten-1` : (selectedProvider === str.key ? `blue--text ` : ``)') {{ str.description }}
- v-list-item-avatar(v-if='selectedProvider === str.key', size='24')
- v-icon.animated.fadeInLeft(color='primary', large) mdi-chevron-right
- v-divider(v-if='idx < providers.length - 1')
-
- v-flex(xs12, lg9)
-
- v-card.animated.fadeInUp.wait-p2s
- v-toolbar(color='primary', dense, flat, dark)
- .subtitle-1 {{provider.title}}
- v-spacer
- v-switch(
- dark
- color='blue lighten-5'
- label='Active'
- v-model='provider.isEnabled'
- hide-details
- inset
- )
- v-card-info(color='blue')
- div
- div {{provider.description}}
- span.caption: a(:href='provider.website') {{provider.website}}
- v-spacer
- .admin-providerlogo
- img(:src='provider.logo', :alt='provider.title')
- v-card-text
- v-form
- .overline.pb-5 {{$t('admin:analytics.providerConfiguration')}}
- .body-1.ml-3(v-if='!provider.config || provider.config.length < 1'): em {{$t('admin:analytics.providerNoConfiguration')}}
- template(v-else, v-for='cfg in provider.config')
- v-select(
- v-if='cfg.value.type === "string" && cfg.value.enum'
- outlined
- :items='cfg.value.enum'
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- prepend-icon='mdi-cog-box'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- :class='cfg.value.hint ? "mb-2" : ""'
- )
- v-switch.mb-3(
- v-else-if='cfg.value.type === "boolean"'
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- color='primary'
- prepend-icon='mdi-cog-box'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- inset
- )
- v-textarea(
- v-else-if='cfg.value.type === "string" && cfg.value.multiline'
- outlined
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- prepend-icon='mdi-cog-box'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- :class='cfg.value.hint ? "mb-2" : ""'
- )
- v-text-field(
- v-else
- outlined
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- prepend-icon='mdi-cog-box'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- :class='cfg.value.hint ? "mb-2" : ""'
- )
-
-
-
-
diff --git a/client/components/admin/admin-api-create.vue b/client/components/admin/admin-api-create.vue
deleted file mode 100644
index 22712bf1..00000000
--- a/client/components/admin/admin-api-create.vue
+++ /dev/null
@@ -1,236 +0,0 @@
-
- div
- v-dialog(v-model='isShown', max-width='650', persistent)
- v-card
- .dialog-header.is-short
- v-icon.mr-3(color='white') mdi-plus
- span {{$t('admin:api.newKeyTitle')}}
- v-card-text.pt-5
- v-text-field(
- outlined
- prepend-icon='mdi-format-title'
- v-model='name'
- :label='$t(`admin:api.newKeyName`)'
- persistent-hint
- ref='keyNameInput'
- :hint='$t(`admin:api.newKeyNameHint`)'
- counter='255'
- )
- v-select.mt-3(
- :items='expirations'
- outlined
- prepend-icon='mdi-clock'
- v-model='expiration'
- :label='$t(`admin:api.newKeyExpiration`)'
- :hint='$t(`admin:api.newKeyExpirationHint`)'
- persistent-hint
- )
- v-divider.mt-4
- v-subheader.pl-2: strong.indigo--text {{$t('admin:api.newKeyPermissionScopes')}}
- v-list.pl-8(nav)
- v-list-item-group(v-model='fullAccess')
- v-list-item(
- :value='true'
- active-class='indigo--text'
- )
- template(v-slot:default='{ active, toggle }')
- v-list-item-action
- v-checkbox(
- :input-value='active'
- :true-value='true'
- color='indigo'
- @click='toggle'
- )
- v-list-item-content
- v-list-item-title {{$t('admin:api.newKeyFullAccess')}}
- v-divider.mt-3
- v-subheader.caption.indigo--text {{$t('admin:api.newKeyGroupPermissions')}}
- v-list-item
- v-select(
- :disabled='fullAccess'
- :items='groups'
- item-text='name'
- item-value='id'
- outlined
- color='indigo'
- v-model='group'
- :label='$t(`admin:api.newKeyGroup`)'
- :hint='$t(`admin:api.newKeyGroupHint`)'
- persistent-hint
- )
- v-card-chin
- v-spacer
- v-btn(text, @click='isShown = false', :disabled='loading') {{$t('common:actions.cancel')}}
- v-btn.px-3(depressed, color='primary', @click='generate', :loading='loading')
- v-icon(left) mdi-chevron-right
- span {{$t('common:actions.generate')}}
-
- v-dialog(
- v-model='isCopyKeyDialogShown'
- max-width='750'
- persistent
- overlay-color='blue darken-5'
- overlay-opacity='.9'
- )
- v-card
- v-toolbar(dense, flat, color='primary', dark) {{$t('admin:api.newKeyTitle')}}
- v-card-text.pt-5
- .body-2.text-center
- i18next(tag='span', path='admin:api.newKeyCopyWarn')
- strong(place='bold') {{$t('admin:api.newKeyCopyWarnBold')}}
- v-textarea.mt-3(
- ref='keyContentsIpt'
- filled
- no-resize
- readonly
- v-model='key'
- :rows='10'
- hide-details
- )
- v-card-chin
- v-spacer
- v-btn.px-3(depressed, dark, color='primary', @click='isCopyKeyDialogShown = false') {{$t('common:actions.close')}}
-
-
-
diff --git a/client/components/admin/admin-api.vue b/client/components/admin/admin-api.vue
deleted file mode 100644
index 3e1f94f2..00000000
--- a/client/components/admin/admin-api.vue
+++ /dev/null
@@ -1,239 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row, wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-rest-api.svg', alt='API', style='width: 80px;')
- .admin-header-title
- .headline.primary--text.animated.fadeInLeft {{$t('admin:api.title')}}
- .subtitle-1.grey--text.animated.fadeInLeft {{$t('admin:api.subtitle')}}
- v-spacer
- template(v-if='enabled')
- status-indicator.mr-3(positive, pulse)
- .caption.green--text.animated.fadeInLeft {{$t('admin:api.enabled')}}
- template(v-else)
- status-indicator.mr-3(negative, pulse)
- .caption.red--text.animated.fadeInLeft {{$t('admin:api.disabled')}}
- v-spacer
- v-btn.mr-3.animated.fadeInDown.wait-p2s(outlined, color='grey', icon, @click='refresh')
- v-icon mdi-refresh
- v-btn.mr-3.animated.fadeInDown.wait-p1s(:color='enabled ? `red` : `green`', depressed, @click='globalSwitch', dark, :loading='isToggleLoading')
- v-icon(left) mdi-power
- span(v-if='!enabled') {{$t('admin:api.enableButton')}}
- span(v-else) {{$t('admin:api.disableButton')}}
- v-btn.animated.fadeInDown(color='primary', depressed, large, @click='newKey', dark)
- v-icon(left) mdi-plus
- span {{$t('admin:api.newKeyButton')}}
- v-card.mt-3.animated.fadeInUp
- v-simple-table(v-if='keys && keys.length > 0')
- template(v-slot:default)
- thead
- tr.grey(:class='$vuetify.theme.dark ? `darken-4-d5` : `lighten-5`')
- th {{$t('admin:api.headerName')}}
- th {{$t('admin:api.headerKeyEnding')}}
- th {{$t('admin:api.headerExpiration')}}
- th {{$t('admin:api.headerCreated')}}
- th {{$t('admin:api.headerLastUpdated')}}
- th(width='100') {{$t('admin:api.headerRevoke')}}
- tbody
- tr(v-for='key of keys', :key='`key-` + key.id')
- td
- strong(:class='key.isRevoked ? `red--text` : ``') {{ key.name }}
- em.caption.ml-1.red--text(v-if='key.isRevoked') (revoked)
- td.caption {{ key.keyShort }}
- td(:style='key.isRevoked ? `text-decoration: line-through;` : ``') {{ key.expiration | moment('LL') }}
- td {{ key.createdAt | moment('calendar') }}
- td {{ key.updatedAt | moment('calendar') }}
- td: v-btn(icon, @click='revoke(key)', :disabled='key.isRevoked'): v-icon(color='error') mdi-cancel
- v-card-text(v-else)
- v-alert.mb-0(icon='mdi-information', :value='true', outlined, color='info') {{$t('admin:api.noKeyInfo')}}
-
- create-api-key(v-model='isCreateDialogShown', @refresh='refresh(false)')
-
- v-dialog(v-model='isRevokeConfirmDialogShown', max-width='500', persistent)
- v-card
- .dialog-header.is-red {{$t('admin:api.revokeConfirm')}}
- v-card-text.pa-4
- i18next(tag='span', path='admin:api.revokeConfirmText')
- strong(place='name') {{ current.name }}
- v-card-actions
- v-spacer
- v-btn(text, @click='isRevokeConfirmDialogShown = false', :disabled='revokeLoading') {{$t('common:actions.cancel')}}
- v-btn(color='red', dark, @click='revokeConfirm', :loading='revokeLoading') {{$t('admin:api.revoke')}}
-
-
-
-
-
diff --git a/client/components/admin/admin-auth.vue b/client/components/admin/admin-auth.vue
deleted file mode 100644
index d30f8e87..00000000
--- a/client/components/admin/admin-auth.vue
+++ /dev/null
@@ -1,433 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row, wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-unlock.svg', alt='Authentication', style='width: 80px;')
- .admin-header-title
- .headline.primary--text.animated.fadeInLeft {{ $t('admin:auth.title') }}
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p4s {{ $t('admin:auth.subtitle') }}
- v-spacer
- v-btn.animated.fadeInDown.wait-p3s(icon, outlined, color='grey', href='https://docs.requarks.io/auth', target='_blank')
- v-icon mdi-help-circle
- v-btn.animated.fadeInDown.wait-p2s.mx-3(icon, outlined, color='grey', @click='refresh')
- v-icon mdi-refresh
- v-btn.animated.fadeInDown(color='success', @click='save', depressed, large)
- v-icon(left) mdi-check
- span {{$t('common:actions.apply')}}
-
- v-flex(lg3, xs12)
- v-card.animated.fadeInUp
- v-toolbar(flat, color='teal', dark, dense)
- .subtitle-1 {{$t('admin:auth.activeStrategies')}}
- v-list(two-line, dense).py-0
- draggable(
- v-model='activeStrategies'
- handle='.is-handle'
- direction='vertical'
- )
- transition-group
- v-list-item(
- v-for='(str, idx) in activeStrategies'
- :key='str.key'
- @click='selectedStrategy = str.key'
- :class='selectedStrategy === str.key ? ($vuetify.theme.dark ? `grey darken-5` : `teal lighten-5`) : ``'
- )
- v-list-item-avatar.is-handle(size='24')
- v-icon(:color='selectedStrategy === str.key ? `teal` : `grey`') mdi-drag-horizontal
- v-list-item-content
- v-list-item-title.body-2(:class='selectedStrategy === str.key ? `teal--text` : ``') {{ str.displayName }}
- v-list-item-subtitle: .caption(:class='selectedStrategy === str.key ? `teal--text ` : ``') {{ str.strategy.title }}
- v-list-item-avatar(v-if='selectedStrategy === str.key', size='24')
- v-icon.animated.fadeInLeft(color='teal', large) mdi-chevron-right
- v-card-chin
- v-menu(offset-y, bottom, min-width='250px', max-width='550px', max-height='50vh', style='flex: 1 1;', center)
- template(v-slot:activator='{ on }')
- v-btn(v-on='on', color='primary', depressed, block)
- v-icon(left) mdi-plus
- span {{$t('admin:auth.addStrategy')}}
- v-list(dense)
- template(v-for='(str, idx) of strategies')
- v-list-item(
- :key='str.key'
- :disabled='str.isDisabled'
- @click='addStrategy(str)'
- )
- v-list-item-avatar(height='24', width='48', tile)
- v-img(:src='str.logo', width='48px', height='24px', contain, :style='str.isDisabled ? `opacity: .25;` : ``')
- v-list-item-content
- v-list-item-title {{str.title}}
- v-list-item-subtitle: .caption(:style='str.isDisabled ? `opacity: .4;` : ``') {{str.description}}
- v-divider(v-if='idx < strategies.length - 1')
-
- v-flex(xs12, lg9)
- v-card.animated.fadeInUp.wait-p2s
- v-toolbar(color='primary', dense, flat, dark)
- .subtitle-1 {{strategy.displayName}} #[em ({{strategy.strategy.title}})]
- v-spacer
- v-btn(small, outlined, dark, color='white', :disabled='strategy.key === `local`', @click='deleteStrategy()')
- v-icon(left) mdi-close
- span {{$t('common:actions.delete')}}
- v-card-info(color='blue')
- div
- span {{strategy.strategy.description}}
- .caption: a(:href='strategy.strategy.website') {{strategy.strategy.website}}
- v-spacer
- .admin-providerlogo
- img(:src='strategy.strategy.logo', :alt='strategy.strategy.title')
- v-card-text
- .row
- .col-8
- v-text-field(
- outlined
- :label='$t(`admin:auth.displayName`)'
- v-model='strategy.displayName'
- prepend-icon='mdi-format-title'
- :hint='$t(`admin:auth.displayNameHint`)'
- persistent-hint
- )
- .col-4
- v-switch.mt-1(
- :label='$t(`admin:auth.strategyIsEnabled`)'
- v-model='strategy.isEnabled'
- color='primary'
- prepend-icon='mdi-power'
- :hint='$t(`admin:auth.strategyIsEnabledHint`)'
- persistent-hint
- inset
- :disabled='strategy.key === `local`'
- )
- template(v-if='strategy.config && Object.keys(strategy.config).length > 0')
- v-divider
- .overline.my-5 {{$t('admin:auth.strategyConfiguration')}}
- .pr-3
- template(v-for='cfg in strategy.config')
- v-select.mb-3(
- v-if='cfg.value.type === "string" && cfg.value.enum'
- outlined
- :items='cfg.value.enum'
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- prepend-icon='mdi-cog-box'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- :class='cfg.value.hint ? "mb-2" : ""'
- :style='cfg.value.maxWidth > 0 ? `max-width:` + cfg.value.maxWidth + `px;` : ``'
- )
- v-switch.mb-6(
- v-else-if='cfg.value.type === "boolean"'
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- color='primary'
- prepend-icon='mdi-cog-box'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- inset
- )
- v-textarea.mb-3(
- v-else-if='cfg.value.type === "string" && cfg.value.multiline'
- outlined
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- prepend-icon='mdi-cog-box'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- :class='cfg.value.hint ? "mb-2" : ""'
- )
- v-text-field.mb-3(
- v-else
- outlined
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- prepend-icon='mdi-cog-box'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- :class='cfg.value.hint ? "mb-2" : ""'
- :style='cfg.value.maxWidth > 0 ? `max-width:` + cfg.value.maxWidth + `px;` : ``'
- )
- v-divider
- .overline.my-5 {{$t('admin:auth.registration')}}
- .pr-3
- v-switch.ml-3(
- v-model='strategy.selfRegistration'
- :label='$t(`admin:auth.selfRegistration`)'
- color='primary'
- :hint='$t(`admin:auth.selfRegistrationHint`)'
- persistent-hint
- inset
- )
- v-combobox.ml-3.mt-5(
- :label='$t(`admin:auth.domainsWhitelist`)'
- v-model='strategy.domainWhitelist'
- prepend-icon='mdi-email-check-outline'
- outlined
- :disabled='!strategy.selfRegistration'
- :hint='$t(`admin:auth.domainsWhitelistHint`)'
- persistent-hint
- small-chips
- deletable-chips
- clearable
- multiple
- chips
- )
- v-autocomplete.mt-3.ml-3(
- outlined
- :disabled='!strategy.selfRegistration'
- :items='groups'
- item-text='name'
- item-value='id'
- :label='$t(`admin:auth.autoEnrollGroups`)'
- v-model='strategy.autoEnrollGroups'
- prepend-icon='mdi-account-group'
- :hint='$t(`admin:auth.autoEnrollGroupsHint`)'
- small-chips
- persistent-hint
- deletable-chips
- clearable
- multiple
- chips
- )
-
- v-card.mt-4.wiki-form.animated.fadeInUp.wait-p4s(v-if='selectedStrategy !== `local`')
- v-toolbar(color='primary', dense, flat, dark)
- .subtitle-1 {{$t('admin:auth.configReference')}}
- v-card-text
- .body-2 {{$t('admin:auth.configReferenceSubtitle')}}
- v-alert.mt-3.radius-7(v-if='host.length < 8', color='red', outlined, :value='true', icon='mdi-alert')
- i18next(path='admin:auth.siteUrlNotSetup', tag='span')
- strong(place='siteUrl') {{$t('admin:general.siteUrl')}}
- strong(place='general') {{$t('admin:general.title')}}
- .pa-3.mt-3.radius-7.grey(v-else, :class='$vuetify.theme.dark ? `darken-3-d5` : `lighten-3`')
- .body-2: strong {{$t('admin:auth.allowedWebOrigins')}}
- .body-2 {{host}}
- v-divider.my-3
- .body-2: strong {{$t('admin:auth.callbackUrl')}}
- .body-2 {{host}}/login/{{strategy.key}}/callback
- v-divider.my-3
- .body-2: strong {{$t('admin:auth.loginUrl')}}
- .body-2 {{host}}/login
- v-divider.my-3
- .body-2: strong {{$t('admin:auth.logoutUrl')}}
- .body-2 {{host}}
- v-divider.my-3
- .body-2: strong {{$t('admin:auth.tokenEndpointAuthMethod')}}
- .body-2 HTTP-POST
-
-
-
diff --git a/client/components/admin/admin-comments.vue b/client/components/admin/admin-comments.vue
deleted file mode 100644
index af960eb8..00000000
--- a/client/components/admin/admin-comments.vue
+++ /dev/null
@@ -1,206 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row, wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-chat-bubble.svg', alt='Comments', style='width: 80px;')
- .admin-header-title
- .headline.primary--text.animated.fadeInLeft {{$t('admin:comments.title')}}
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s {{$t('admin:comments.subtitle')}}
- v-spacer
- v-btn.animated.fadeInDown.wait-p3s(icon, outlined, color='grey', href='https://docs.requarks.io/comments', target='_blank')
- v-icon mdi-help-circle
- v-btn.mx-3.animated.fadeInDown.wait-p2s(icon, outlined, color='grey', @click='refresh')
- v-icon mdi-refresh
- v-btn.animated.fadeInDown(color='success', @click='save', depressed, large)
- v-icon(left) mdi-check
- span {{$t('common:actions.apply')}}
-
- v-flex(lg3, xs12)
- v-card.animated.fadeInUp
- v-toolbar(flat, color='primary', dark, dense)
- .subtitle-1 {{$t('admin:comments.provider')}}
- v-list.py-0(two-line, dense)
- template(v-for='(provider, idx) in providers')
- v-list-item(:key='provider.key', @click='selectedProvider = provider.key', :disabled='!provider.isAvailable')
- v-list-item-avatar(size='24')
- v-icon(color='grey', v-if='!provider.isAvailable') mdi-minus-box-outline
- v-icon(color='primary', v-else-if='provider.key === selectedProvider') mdi-checkbox-marked-circle-outline
- v-icon(color='grey', v-else) mdi-checkbox-blank-circle-outline
- v-list-item-content
- v-list-item-title.body-2(:class='!provider.isAvailable ? `grey--text` : (selectedProvider === provider.key ? `primary--text` : ``)') {{ provider.title }}
- v-list-item-subtitle: .caption(:class='!provider.isAvailable ? `grey--text text--lighten-1` : (selectedProvider === provider.key ? `blue--text ` : ``)') {{ provider.description }}
- v-list-item-avatar(v-if='selectedProvider === provider.key', size='24')
- v-icon.animated.fadeInLeft(color='primary', large) mdi-chevron-right
- v-divider(v-if='idx < providers.length - 1')
-
- v-flex(lg9, xs12)
- v-card.animated.fadeInUp.wait-p2s
- v-toolbar(color='primary', dense, flat, dark)
- .subtitle-1 {{provider.title}}
- v-card-info(color='blue')
- div
- div {{provider.description}}
- span.caption: a(:href='provider.website') {{provider.website}}
- v-spacer
- .admin-providerlogo
- img(:src='provider.logo', :alt='provider.title')
- v-card-text
- .overline.my-5 {{$t('admin:comments.providerConfig')}}
- .body-2.ml-3(v-if='!provider.config || provider.config.length < 1'): em {{$t('admin:comments.providerNoConfig')}}
- template(v-else, v-for='cfg in provider.config')
- v-select.mb-3(
- v-if='cfg.value.type === "string" && cfg.value.enum'
- outlined
- :items='cfg.value.enum'
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- prepend-icon='mdi-cog-box'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- :class='cfg.value.hint ? "mb-2" : ""'
- :style='cfg.value.maxWidth > 0 ? `max-width:` + cfg.value.maxWidth + `px;` : ``'
- )
- v-switch.mb-6(
- v-else-if='cfg.value.type === "boolean"'
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- color='primary'
- prepend-icon='mdi-cog-box'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- inset
- )
- v-textarea.mb-3(
- v-else-if='cfg.value.type === "string" && cfg.value.multiline'
- outlined
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- prepend-icon='mdi-cog-box'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- :class='cfg.value.hint ? "mb-2" : ""'
- )
- v-text-field.mb-3(
- v-else
- outlined
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- prepend-icon='mdi-cog-box'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- :class='cfg.value.hint ? "mb-2" : ""'
- :style='cfg.value.maxWidth > 0 ? `max-width:` + cfg.value.maxWidth + `px;` : ``'
- )
-
-
-
diff --git a/client/components/admin/admin-contribute.vue b/client/components/admin/admin-contribute.vue
deleted file mode 100644
index e13507fa..00000000
--- a/client/components/admin/admin-contribute.vue
+++ /dev/null
@@ -1,256 +0,0 @@
-
- v-container.admin-contribute(fluid, grid-list-lg)
- v-layout(row, wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-heart-health.svg', alt='Contribute', style='width: 80px;')
- .admin-header-title
- .headline.primary--text.animated.fadeInLeft {{ $t('admin:contribute.title') }}
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p4s {{ $t('admin:contribute.subtitle') }}
- v-card.mt-3.animated.fadeInUp
- v-card-text
- i18next.body-2.pl-3(path='admin:contribute.openSource', tag='div')
- v-icon(color='red') mdi-heart
- a(href='https://requarks.io', target='_blank') requarks.io
- a(href='https://github.com/Requarks/wiki/graphs/contributors', target='_blank') {{ $t('admin:contribute.openSourceContributors') }}
- .body-2.pt-3.pl-3 {{ $t('admin:contribute.needYourHelp') }}
- v-divider.mt-3
- v-subheader.subtitle-2 {{ $t('admin:contribute.fundOurWork') }}
- v-tabs.mx-3.radius-7.admin-contribute-tabs(
- centered
- fixed-tabs
- background-color='primary'
- color='white'
- dark
- slider-color='#FFF'
- icons-and-text
- )
- v-tab
- span GitHub
- v-icon.my-1(size='24') mdi-github
- v-tab
- span Patreon
- img.my-1(src='/_assets/svg/icon-patreon.svg', style='height: 24px;')
- v-tab
- span OpenCollective
- img.my-1(src='/_assets/svg/icon-opencollective.svg', style='height: 24px;')
- v-tab
- span PayPal
- img.my-1(src='/_assets/svg/icon-paypal.svg', style='height: 24px;')
- v-tab
- span Ethereum
- img.my-1(src='/_assets/svg/icon-ethereum.svg', style='height: 24px;')
- v-tab
- span T-Shirts
- img.my-1(src='/_assets/svg/icon-t-shirt.svg', style='height: 24px;')
- v-tab-item(:transition='false', :reverse-transition='false')
- .body-2.pa-3 {{ $t('admin:contribute.github') }}
- a.ml-3(href='https://github.com/users/NGPixel/sponsorship', :title='$t(`admin:contribute.becomeASponsor`)')
- img(src='/_assets/img/donate_github.svg', :alt='$t(`admin:contribute.becomeASponsor`)' style='width:200px;')
- v-tab-item(:transition='false', :reverse-transition='false')
- .body-2.pa-3 {{ $t('admin:contribute.patreon') }}
- a.ml-3(href='https://www.patreon.com/bePatron?u=16744039', :title='$t(`admin:contribute.becomeAPatron`)')
- img(src='/_assets/img/donate_patreon.png', :alt='$t(`admin:contribute.becomeAPatron`)' style='width:200px;')
- v-tab-item(:transition='false', :reverse-transition='false')
- .body-2.pa-3 {{ $t('admin:contribute.openCollective') }}
- a.ml-3(href='https://opencollective.com/wikijs/donate', :title='$t(`admin:contribute.makeADonation`)')
- img(src='/_assets/img/donate_opencollective.png', :alt='$t(`admin:contribute.makeADonation`)' style='width:300px;')
- v-tab-item(:transition='false', :reverse-transition='false')
- .body-2.pa-3 {{ $t('admin:contribute.paypal') }}
- .ml-3
- form(action='https://www.paypal.com/cgi-bin/webscr', method='post', target='_top')
- input(type='hidden', name='cmd', value='_s-xclick')
- input(type='hidden', name='hosted_button_id', value='FLV5X255Z9CJU')
- input(type='image', src='/_assets/img/donate_paypal.png', border='0', name='submit', title='PayPal - The safer, easier way to pay online!', alt='Donate with PayPal button')
- img(alt='', border='0', src='https://www.paypal.com/en_CA/i/scr/pixel.gif', width='1', height='1')
- v-tab-item(:transition='false', :reverse-transition='false')
- .body-2.pa-3 {{ $t('admin:contribute.ethereum') }}
- .ml-3
- .admin-contribute-ethaddress
- strong Ethereum Address
- span 0xE1d55C19aE86f6Bcbfb17e7f06aCe96BdBb22Cb5
- div: img(src='/_assets/img/donate_eth_qr.png')
- v-tab-item(:transition='false', :reverse-transition='false')
- .body-2.pa-3 {{ $t('admin:contribute.tshirts') }}
- v-card-actions.ml-2
- v-btn(outlined, :color='$vuetify.theme.dark ? `blue lighten-1` : `primary`', href='https://wikijs.threadless.com', large)
- v-icon(left) mdi-tshirt-crew
- span {{ $t('admin:contribute.shop') }}
- v-divider.mt-3
- v-subheader.subtitle-2 {{ $t('admin:contribute.contribute') }}
- .body-2.pl-3
- ul
- i18next(path='admin:contribute.submitAnIdea', tag='li')
- a(href='https://requests.requarks.io/wiki', target='_blank') {{ $t('admin:contribute.submitAnIdeaLink') }}
- i18next(path='admin:contribute.foundABug', tag='li')
- a(href='https://github.com/Requarks/wiki/issues', target='_blank') Github
- i18next(path='admin:contribute.helpTranslate', tag='li')
- a(href='https://wiki.requarks.io/slack', target='_blank') Slack
- v-divider.mt-3
- v-subheader.subtitle-2 {{ $t('admin:contribute.spreadTheWord') }}
- .body-2.pl-3
- ul
- li {{ $t('admin:contribute.talkToFriends') }}
- i18next(path='admin:contribute.followUsOnTwitter', tag='li')
- a(href='https://twitter.com/requarks', target='_blank') Twitter
- v-toolbar(color='indigo', dense, dark)
- .subtitle-1 Sponsors & Backers
- v-container.pa-5.grey(fluid, :class='$vuetify.theme.dark ? `darken-3` : `lighten-4`')
- v-progress-circular(indeterminate, color='indigo', size='24', width='2', v-if='backers.length < 1')
- v-row(dense)
- v-col(cols='12', lg='6', xl='4', v-for='(backer, idx) in backers', :key='backer.id')
- v-card.grey(flat, :class='$vuetify.theme.dark ? `darken-4` : `lighten-2`')
- v-list-item
- v-list-item-avatar
- img(v-if='backer.avatar', :src='backer.avatar')
- v-avatar(v-else, color='blue-grey', size='40')
- span.white--text.subtitle-1 {{backer.name[0].toUpperCase()}}
- v-list-item-content
- v-list-item-title {{backer.name}}
- v-list-item-subtitle: .caption Since {{backer.joined | moment('MMMM DD, YYYY')}} on {{backer.source}}
- v-list-item-action(v-if='backer.twitter')
- v-btn(icon, :href='backer.twitter', target='_blank')
- v-icon(color='grey') mdi-twitter
- v-list-item-action(v-if='backer.website')
- v-btn(icon, :href='backer.website', target='_blank')
- v-icon(color='grey') mdi-earth
- v-toolbar(color='primary', dense, dark)
- .subtitle-1 Special Thanks
- v-list(two-line)
- v-list-item
- v-list-item-avatar
- img(src='https://static.requarks.io/logo/algolia.svg', alt='Algolia')
- v-list-item-content
- v-list-item-title Algolia
- v-list-item-subtitle Algolia is a powerful search-as-a-service solution, made easy to use with API clients, UI libraries, and pre-built integrations.
- v-list-item-action
- v-btn(icon, href='https://www.algolia.com/', target='_blank')
- v-icon(color='grey') mdi-earth
- v-divider
- v-list-item
- v-list-item-avatar
- img(src='https://static.requarks.io/logo/browserstack.svg', alt='Browserstack')
- v-list-item-content
- v-list-item-title BrowserStack
- v-list-item-subtitle BrowserStack is a cloud web and mobile testing platform that enables developers to test their websites and mobile applications.
- v-list-item-action
- v-btn(icon, href='https://www.browserstack.com/', target='_blank')
- v-icon(color='grey') mdi-earth
- v-divider
- v-list-item
- v-list-item-avatar
- img(src='https://static.requarks.io/logo/cloudflare.svg', alt='Cloudflare')
- v-list-item-content
- v-list-item-title Cloudflare
- v-list-item-subtitle Providing content delivery network services, DDoS mitigation, Internet security and distributed domain name server services.
- v-list-item-action
- v-btn(icon, href='https://www.cloudflare.com/', target='_blank')
- v-icon(color='grey') mdi-earth
- v-divider
- v-list-item
- v-list-item-avatar
- img(src='https://static.requarks.io/logo/digitalocean.svg', alt='DigitalOcean')
- v-list-item-content
- v-list-item-title DigitalOcean
- v-list-item-subtitle Providing developers and businesses a reliable, easy-to-use cloud computing platform of virtual servers (Droplets), object storage (Spaces), and more.
- v-list-item-action
- v-btn(icon, href='https://m.do.co/c/5f7445bfa4d0', target='_blank')
- v-icon(color='grey') mdi-earth
- v-divider
- v-list-item
- v-list-item-avatar(tile)
- img(src='/_assets/svg/logo-icons8.svg', alt='Icons8')
- v-list-item-content
- v-list-item-title Icons8
- v-list-item-subtitle All the Icons You Need. Guaranteed.
- v-list-item-action
- v-btn(icon, href='https://icons8.com', target='_blank')
- v-icon(color='grey') mdi-earth
- v-divider
- v-list-item
- v-list-item-avatar(tile)
- img(src='https://static.requarks.io/logo/lokalise.png', alt='Lokalise')
- v-list-item-content
- v-list-item-title Lokalise
- v-list-item-subtitle Lokalise is a translation management system built for agile teams who want to automate their localization process.
- v-list-item-action
- v-btn(icon, href='https://lokalise.co', target='_blank')
- v-icon(color='grey') mdi-earth
- v-divider
- v-list-item
- v-list-item-avatar(tile)
- img(src='https://static.requarks.io/logo/netlify.svg', alt='Netlify')
- v-list-item-content
- v-list-item-title Netlify
- v-list-item-subtitle Deploy modern static websites with Netlify. Get CDN, Continuous deployment, 1-click HTTPS, and all the services you need.
- v-list-item-action
- v-btn(icon, href='https://wwwnetlify.com', target='_blank')
- v-icon(color='grey') mdi-earth
-
-
-
-
-
-
diff --git a/client/components/admin/admin-dashboard.vue b/client/components/admin/admin-dashboard.vue
deleted file mode 100644
index 9dfa9a97..00000000
--- a/client/components/admin/admin-dashboard.vue
+++ /dev/null
@@ -1,256 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row, wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-browse-page.svg', alt='Dashboard', style='width: 80px;')
- .admin-header-title
- .headline.primary--text.animated.fadeInLeft {{ $t('admin:dashboard.title') }}
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s {{ $t('admin:dashboard.subtitle') }}
- v-flex(xs12 md6 lg4 xl3 d-flex)
- v-card.primary.dashboard-card.animated.fadeInUp(dark)
- v-card-text
- v-icon.dashboard-icon mdi-file-document-outline
- .overline {{$t('admin:dashboard.pages')}}
- animated-number.display-1(
- :value='info.pagesTotal'
- :duration='2000'
- :formatValue='round'
- easing='easeOutQuint'
- )
- v-flex(xs12 md6 lg4 xl3 d-flex)
- 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')}}
- animated-number.display-1(
- :value='info.usersTotal'
- :duration='2000'
- :formatValue='round'
- easing='easeOutQuint'
- )
- v-flex(xs12 md6 lg4 xl3 d-flex)
- 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')}}
- animated-number.display-1(
- :value='info.groupsTotal'
- :duration='2000'
- :formatValue='round'
- easing='easeOutQuint'
- )
- v-flex(xs12 md6 lg12 xl3 d-flex)
- v-card.dashboard-card.animated.fadeInUp.wait-p6s(
- :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 ? `green` : `red darken-4`', small) mdi-wrench
- v-card-text
- v-icon.dashboard-icon mdi-blur
- .subtitle-1 Wiki.js {{info.currentVersion}}
- .body-2(v-if='isLatestVersion') {{$t('admin:dashboard.versionLatest')}}
- .body-2(v-else) {{$t('admin:dashboard.versionNew', { version: info.latestVersion })}}
- v-flex(xs12, xl6)
- v-card.radius-7.animated.fadeInUp.wait-p2s
- v-toolbar(:color='$vuetify.theme.dark ? `grey darken-2` : `grey lighten-5`', dense, flat)
- v-spacer
- .overline {{$t('admin:dashboard.recentPages')}}
- v-spacer
- v-data-table.pb-2(
- :items='recentPages'
- :headers='recentPagesHeaders'
- :loading='recentPagesLoading'
- hide-default-footer
- hide-default-header
- )
- template(slot='item', slot-scope='props')
- tr.is-clickable(:active='props.selected', @click='$router.push(`/pages/` + props.item.id)')
- td
- .body-2: strong {{ props.item.title }}
- td.admin-pages-path
- v-chip(label, small, :color='$vuetify.theme.dark ? `grey darken-4` : `grey lighten-4`') {{ props.item.locale }}
- span.ml-2.grey--text(:class='$vuetify.theme.dark ? `text--lighten-1` : `text--darken-2`') / {{ props.item.path }}
- td.text-right.caption(width='250') {{ props.item.updatedAt | moment('calendar') }}
- v-flex(xs12, xl6)
- v-card.radius-7.animated.fadeInUp.wait-p4s
- v-toolbar(:color='$vuetify.theme.dark ? `grey darken-2` : `grey lighten-5`', dense, flat)
- v-spacer
- .overline {{$t('admin:dashboard.lastLogins')}}
- v-spacer
- v-data-table.pb-2(
- :items='lastLogins'
- :headers='lastLoginsHeaders'
- :loading='lastLoginsLoading'
- hide-default-footer
- hide-default-header
- )
- template(slot='item', slot-scope='props')
- tr.is-clickable(:active='props.selected', @click='$router.push(`/users/` + props.item.id)')
- td
- .body-2: strong {{ props.item.name }}
- td.text-right.caption(width='250') {{ props.item.lastLoginAt | moment('calendar') }}
-
- v-flex(xs12)
- v-card.dashboard-contribute.animated.fadeInUp.wait-p4s
- v-card-text
- img(src='/_assets/svg/icon-heart-health.svg', alt='Contribute', style='height: 80px;')
- .pl-5
- .subtitle-1 {{$t('admin:contribute.title')}}
- .body-2.mt-3: strong {{$t('admin:dashboard.contributeSubtitle')}}
- .body-2 {{$t('admin:dashboard.contributeHelp')}}
- v-btn.mx-0.mt-4(:color='$vuetify.theme.dark ? `indigo lighten-3` : `indigo`', outlined, small, to='/contribute')
- .caption: strong {{$t('admin:dashboard.contributeLearnMore')}}
-
-
-
-
-
-
diff --git a/client/components/admin/admin-dev-flags.vue b/client/components/admin/admin-dev-flags.vue
deleted file mode 100644
index f345ea7d..00000000
--- a/client/components/admin/admin-dev-flags.vue
+++ /dev/null
@@ -1,94 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row, wrap)
- v-flex(xs12)
- .admin-header
- img(src='/_assets/svg/icon-console.svg', alt='Developer Tools', style='width: 80px;')
- .admin-header-title
- .headline.primary--text Developer Tools
- .subtitle-1.grey--text Flags
- v-spacer
- v-btn(color='success', depressed, @click='save', large)
- v-icon(left) mdi-check
- span {{$t('common:actions.apply')}}
-
- v-card.mt-3(:class='$vuetify.theme.dark ? `grey darken-3-d5` : `white grey--text text--darken-3`')
- v-alert(color='red', :value='true', icon='mdi-alert', dark, prominent)
- span Do NOT enable these flags unless you know what you're doing!
- .caption Doing so may result in data loss or broken installation!
- v-card-text
- v-switch.mt-3(
- color='primary'
- hint='Log detailed debug info on LDAP/AD login attempts.'
- persistent-hint
- label='LDAP Debug'
- v-model='flags.ldapdebug'
- inset
- )
- v-divider.mt-3
- v-switch.mt-3(
- color='red'
- hint='Log all queries made to the database to console.'
- persistent-hint
- label='SQL Query Logging'
- v-model='flags.sqllog'
- inset
- )
-
-
-
-
-
diff --git a/client/components/admin/admin-editor.vue b/client/components/admin/admin-editor.vue
deleted file mode 100644
index c9e59713..00000000
--- a/client/components/admin/admin-editor.vue
+++ /dev/null
@@ -1,66 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row, wrap)
- v-flex(xs12)
- .admin-header
- img(src='/_assets/svg/icon-web-design.svg', alt='Editor', style='width: 80px;')
- .admin-header-title
- .headline.primary--text Editor
- .subtitle-1.grey--text Configure the content editors #[v-chip(label, color='primary', small).white--text coming soon]
- v-spacer
- v-btn(outline, color='grey', @click='refresh', large)
- v-icon refresh
- v-btn(color='success', @click='save', depressed, large)
- v-icon(left) check
- span {{$t('common:actions.apply')}}
-
- v-card.mt-3
- v-tabs(color='grey darken-2', fixed-tabs, slider-color='white', show-arrows, dark)
- v-tab(key='settings'): v-icon settings
- v-tab(key='code') Markdown
-
- v-tab-item(key='settings', :transition='false', :reverse-transition='false')
- v-card.pa-3(flat, tile)
- .body-2.grey--text.text--darken-1 Select which editors to enable:
- .caption.grey--text.pb-2 Some editors require additional configuration in their dedicated tab (when selected).
- v-form
- v-checkbox.my-0(
- v-for='editor in editors'
- v-model='editor.isEnabled'
- :key='editor.key'
- :label='editor.title'
- color='primary'
- disabled
- hide-details
- )
- v-tab-item(key='code', :transition='false', :reverse-transition='false')
- v-card.wiki-form.pa-3(flat, tile)
- v-form
- v-subheader Editor Configuration
- .body-1.ml-3 This editor has no configuration options you can modify.
-
-
-
-
-
diff --git a/client/components/admin/admin-extensions.vue b/client/components/admin/admin-extensions.vue
deleted file mode 100644
index 1e0fdfa2..00000000
--- a/client/components/admin/admin-extensions.vue
+++ /dev/null
@@ -1,119 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-installing-updates.svg', alt='Extensions', style='width: 80px;')
- .admin-header-title
- .headline.primary--text.animated.fadeInLeft {{ $t('admin:extensions.title') }}
- .subtitle-1.grey--text.animated.fadeInLeft {{ $t('admin:extensions.subtitle') }}
- v-form.pt-3
- v-layout(row wrap)
- v-flex(xl6 lg8 xs12)
- v-alert.mb-4(outlined, color='error', icon='mdi-alert')
- span New extensions cannot be installed at the moment. This feature is coming in a future release.
- v-expansion-panels.admin-extensions-exp(hover, popout)
- v-expansion-panel(v-for='ext of extensions', :key='`ext-` + ext.key')
- v-expansion-panel-header(disable-icon-rotate)
- span {{ext.title}}
- template(v-slot:actions)
- v-chip(label, color='success', small, v-if='ext.isInstalled') Installed
- v-chip(label, color='warning', small, v-else) Not Installed
- v-expansion-panel-content.pa-0
- v-card(flat, :class='$vuetify.theme.dark ? `grey darken-3` : `grey lighten-5`', tile)
- v-card-text
- .body-2 {{ext.description}}
- v-divider.my-4
- .body-2
- strong.mr-2 This extension is
- v-chip.mr-2(v-if='ext.isCompatible', label, outlined, small, color='success') compatible
- v-chip.mr-2(v-else, label, small, color='error') not compatible
- strong with your host.
- v-card-chin
- v-spacer
- v-btn(disabled)
- v-icon(left) mdi-plus
- span Install
-
-
-
-
-
diff --git a/client/components/admin/admin-general.vue b/client/components/admin/admin-general.vue
deleted file mode 100644
index 24829c8c..00000000
--- a/client/components/admin/admin-general.vue
+++ /dev/null
@@ -1,369 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-categorize.svg', alt='General', style='width: 80px;')
- .admin-header-title
- .headline.primary--text.animated.fadeInLeft {{ $t('admin:general.title') }}
- .subtitle-1.grey--text.animated.fadeInLeft {{ $t('admin:general.subtitle') }}
- v-spacer
- v-btn.animated.fadeInDown(color='success', depressed, @click='save', large)
- v-icon(left) mdi-check
- span {{$t('common:actions.apply')}}
- v-form.pt-3
- v-layout(row wrap)
- v-flex(lg6 xs12)
- v-form
- v-card.animated.fadeInUp
- v-toolbar(color='primary', dark, dense, flat)
- v-toolbar-title.subtitle-1 {{ $t('admin:general.siteInfo') }}
- .overline.grey--text.pa-4 {{$t('admin:general.general')}}
- .px-3.pb-3
- v-text-field(
- outlined
- :label='$t(`admin:general.siteUrl`)'
- required
- :counter='255'
- v-model='config.host'
- prepend-icon='mdi-label-variant-outline'
- :hint='$t(`admin:general.siteUrlHint`)'
- persistent-hint
- )
- v-text-field.mt-3(
- outlined
- :label='$t(`admin:general.siteTitle`)'
- required
- :counter='50'
- v-model='config.title'
- prepend-icon='mdi-earth'
- :hint='$t(`admin:general.siteTitleHint`)'
- persistent-hint
- )
- v-divider
- .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
- v-text-field(
- outlined
- :label='$t(`admin:general.companyName`)'
- v-model='config.company'
- :counter='255'
- prepend-icon='mdi-domain'
- persistent-hint
- :hint='$t(`admin:general.companyNameHint`)'
- )
- v-select.mt-3(
- outlined
- :label='$t(`admin:general.contentLicense`)'
- :items='contentLicenses'
- v-model='config.contentLicense'
- prepend-icon='mdi-creative-commons'
- :return-object='false'
- :hint='$t(`admin:general.contentLicenseHint`)'
- persistent-hint
- )
- v-divider
- .overline.grey--text.pa-4 SEO
- .px-3.pb-3
- v-text-field(
- outlined
- :label='$t(`admin:general.siteDescription`)'
- :counter='255'
- v-model='config.description'
- prepend-icon='mdi-compass'
- :hint='$t(`admin:general.siteDescriptionHint`)'
- persistent-hint
- )
- v-select.mt-3(
- outlined
- :label='$t(`admin:general.metaRobots`)'
- multiple
- :items='metaRobots'
- v-model='config.robots'
- prepend-icon='mdi-compass'
- :return-object='false'
- :hint='$t(`admin:general.metaRobotsHint`)'
- persistent-hint
- )
-
- v-flex(lg6 xs12)
- v-card.animated.fadeInUp.wait-p4s
- v-toolbar(color='indigo', dark, dense, flat)
- v-toolbar-title.subtitle-1 Features
- v-card-text
- //- v-switch(
- //- inset
- //- label='Asset Image Optimization'
- //- color='indigo'
- //- v-model='config.featureTinyPNG'
- //- persistent-hint
- //- hint='Image optimization tool to reduce filesize and bandwidth costs.'
- //- disabled
- //- )
- //- v-text-field.mt-3(
- //- outlined
- //- label='TinyPNG API Key'
- //- :counter='255'
- //- v-model='config.description'
- //- prepend-icon='mdi-subdirectory-arrow-right'
- //- hint='Get your API key at https://tinypng.com/developers'
- //- persistent-hint
- //- disabled
- //- )
-
- //- v-divider.mt-3
- //- v-switch(
- //- inset
- //- label='Page Ratings'
- //- color='indigo'
- //- v-model='config.featurePageRatings'
- //- persistent-hint
- //- hint='Allow users to rate pages.'
- //- disabled
- //- )
-
- //- v-divider.mt-3
- v-switch(
- inset
- label='Comments'
- color='indigo'
- v-model='config.featurePageComments'
- persistent-hint
- hint='Allow users to leave comments on pages.'
- )
-
- //- v-divider.mt-3
- //- v-switch(
- //- inset
- //- label='Personal Wikis'
- //- color='indigo'
- //- v-model='config.featurePersonalWikis'
- //- persistent-hint
- //- hint='Allow users to have their own personal wiki.'
- //- disabled
- //- )
-
- component(:is='activeModal')
-
-
-
-
-
-
diff --git a/client/components/admin/admin-groups-edit-permissions.vue b/client/components/admin/admin-groups-edit-permissions.vue
deleted file mode 100644
index 91b1b684..00000000
--- a/client/components/admin/admin-groups-edit-permissions.vue
+++ /dev/null
@@ -1,224 +0,0 @@
-
- v-card(flat)
- v-container.px-3.pb-3.pt-3(fluid, grid-list-md)
- v-layout(row, wrap)
- v-flex(xs12, v-if='group.isSystem')
- v-alert.radius-7.mb-0(
- color='orange darken-2'
- :class='$vuetify.theme.dark ? "grey darken-4" : "orange lighten-5"'
- outlined
- :value='true'
- icon='mdi-lock-outline'
- ) This is a system group. Some permissions cannot be modified.
- v-flex(xs12, md6, lg4, v-for='pmGroup in permissions', :key='pmGroup.category')
- v-card.md2(flat, :class='$vuetify.theme.dark ? "grey darken-3-d5" : "grey lighten-5"')
- .overline.px-5.pt-5.pb-3.grey--text.text--darken-2 {{pmGroup.category}}
- v-card-text.pt-0
- template(v-for='(pm, idx) in pmGroup.items')
- v-checkbox.pt-0(
- style='justify-content: space-between;'
- :key='pm.permission'
- :label='pm.permission'
- :hint='pm.hint'
- persistent-hint
- color='primary'
- v-model='group.permissions'
- :value='pm.permission'
- :append-icon='pm.warning ? "mdi-alert" : null',
- :disabled='(group.isSystem && pm.restrictedForSystem) || group.id === 1 || pm.disabled'
- )
- v-divider.mt-3(v-if='idx < pmGroup.items.length - 1')
-
-
-
diff --git a/client/components/admin/admin-groups-edit-rules.vue b/client/components/admin/admin-groups-edit-rules.vue
deleted file mode 100644
index 89d97db4..00000000
--- a/client/components/admin/admin-groups-edit-rules.vue
+++ /dev/null
@@ -1,336 +0,0 @@
-
- v-card(flat)
- v-card-text(v-if='group.id === 1')
- v-alert.radius-7.mb-0(
- :class='$vuetify.theme.dark ? "grey darken-4" : "orange lighten-5"'
- color='orange darken-2'
- outlined
- icon='mdi-lock-outline'
- ) This group has access to everything.
- template(v-else)
- v-card-title(:class='$vuetify.theme.dark ? `grey darken-3-d5` : ``')
- v-alert.radius-7.caption(
- :class='$vuetify.theme.dark ? `grey darken-3-d3` : `grey lighten-4`'
- color='grey'
- outlined
- icon='mdi-information'
- ) You must enable global content permissions (under Permissions tab) for page rules to have any effect.
- v-spacer
- v-btn.mx-2(depressed, color='primary', @click='addRule')
- v-icon(left) mdi-plus
- | Add Rule
- v-menu(
- right
- offset-y
- nudge-left='115'
- )
- template(v-slot:activator='{ on }')
- v-btn.is-icon(v-on='on', outlined, color='primary')
- v-icon mdi-dots-horizontal
- v-list(dense)
- v-list-item(@click='comingSoon')
- v-list-item-avatar
- v-icon mdi-application-import
- v-list-item-title Load Preset
- v-divider
- v-list-item(@click='comingSoon')
- v-list-item-avatar
- v-icon mdi-application-export
- v-list-item-title Save As Preset
- v-divider
- v-list-item(@click='comingSoon')
- v-list-item-avatar
- v-icon mdi-cloud-upload
- v-list-item-title Import Rules
- v-divider
- v-list-item(@click='comingSoon')
- v-list-item-avatar
- v-icon mdi-cloud-download
- v-list-item-title Export Rules
- v-card-text(:class='$vuetify.theme.dark ? `grey darken-4-l5` : `white`')
- .rules
- .caption(v-if='group.pageRules.length === 0')
- em(:class='$vuetify.theme.dark ? `grey--text` : `blue-grey--text`') This group has no page rules yet.
- .rule(v-for='rule of group.pageRules', :key='rule.id')
- v-btn.ma-0.radius-4.rule-deny-btn(
- solo
- :color='rule.deny ? "red" : "green"'
- dark
- @click='rule.deny = !rule.deny'
- height='48'
- )
- v-icon(v-if='rule.deny') mdi-cancel
- v-icon(v-else) mdi-check-circle
- //- Roles
- v-select.ml-1(
- solo
- :items='roles'
- v-model='rule.roles'
- placeholder='Select Role(s)...'
- hide-details
- multiple
- chips
- deletable-chips
- small-chips
- height='48px'
- style='flex: 0 1 440px;'
- :menu-props='{ "maxHeight": 500 }'
- clearable
- dense
- )
- template(slot='selection', slot-scope='{ item, index }')
- v-chip.white--text.ml-0(v-if='index <= 1', small, label, :color='rule.deny ? `red` : `green`').caption {{ item.value }}
- v-chip.white--text.ml-0(v-if='index === 2', small, label, :color='rule.deny ? `red lighten-2` : `green lighten-2`').caption + {{ rule.roles.length - 2 }} more
- template(slot='item', slot-scope='props')
- v-list-item-action(style='min-width: 30px;')
- v-checkbox(
- v-model='props.attrs.inputValue'
- hide-details
- color='primary'
- )
- v-icon.mr-2(:color='rule.deny ? `red` : `green`') {{props.item.icon}}
- v-list-item-content
- v-list-item-title.body-2 {{props.item.text}}
- v-chip.mr-2.grey--text(label, small, :color='$vuetify.theme.dark ? `grey darken-4` : `grey lighten-4`').caption {{props.item.value}}
-
- //- Match
- v-select.ml-1.mr-1(
- solo
- :items='matches'
- v-model='rule.match'
- placeholder='Match...'
- hide-details
- height='48px'
- style='flex: 0 1 250px;'
- dense
- )
- template(slot='selection', slot-scope='{ item, index }')
- .body-2 {{item.text}}
- template(slot='item', slot-scope='data')
- v-list-item-avatar
- v-avatar.white--text.radius-4(color='blue', size='30', tile) {{ data.item.icon }}
- v-list-item-content
- v-list-item-title(v-html='data.item.text')
- //- Locales
- v-select.mr-1(
- :background-color='$vuetify.theme.dark ? `grey darken-3-d5` : `blue-grey lighten-5`'
- solo
- :items='locales'
- v-model='rule.locales'
- placeholder='Any Locale'
- item-value='code'
- item-text='name'
- multiple
- hide-details
- height='48px'
- dense
- :menu-props='{ "minWidth": 250 }'
- style='flex: 0 1 150px;'
- )
- template(slot='selection', slot-scope='{ item, index }')
- v-chip.white--text.ml-0(v-if='rule.locales.length === 1', small, label, :color='rule.deny ? `red` : `green`').caption {{ item.code.toUpperCase() }}
- v-chip.white--text.ml-0(v-else-if='index === 0', small, label, :color='rule.deny ? `red` : `green`').caption {{ rule.locales.length }} locales
- v-list-item(slot='prepend-item', @click='rule.locales = []')
- v-list-item-action(style='min-width: 30px;')
- v-checkbox(
- :input-value='rule.locales.length === 0'
- hide-details
- color='primary'
- readonly
- )
- v-icon.mr-2(:color='rule.deny ? `red` : `green`') mdi-earth
- v-list-item-content
- v-list-item-title.body-2 Any Locale
- v-divider(slot='prepend-item')
- template(slot='item', slot-scope='props')
- v-list-item-action(style='min-width: 30px;')
- v-checkbox(
- v-model='props.attrs.inputValue'
- hide-details
- color='primary'
- )
- v-icon.mr-2(:color='rule.deny ? `red` : `green`') mdi-web
- v-list-item-content
- v-list-item-title.body-2 {{props.item.name}}
- v-chip.mr-2.grey--text(label, small, :color='$vuetify.theme.dark ? `grey darken-4` : `grey lighten-4`').caption {{props.item.code.toUpperCase()}}
-
- //- Path
- v-text-field(
- solo
- v-model='rule.path'
- label='Path'
- :prefix='(rule.match !== `END` && rule.match !== `TAG`) ? `/` : null'
- :placeholder='rule.match === `REGEX` ? `Regular Expression` : rule.match === `TAG` ? `Tag` : `Path`'
- :suffix='rule.match === `REGEX` ? `/` : null'
- hide-details
- :color='$vuetify.theme.dark ? `grey` : `blue-grey`'
- )
-
- v-btn.ml-2(icon, @click='removeRule(rule.id)', small)
- v-icon(:color='$vuetify.theme.dark ? `grey` : `blue-grey`') mdi-close
-
- v-divider.mt-3
- .overline.py-3 Rules Order
- .body-2.pl-3 Rules are applied in order of path specificity. A more precise path will always override a less defined path.
- .body-2.pl-5 For example, #[span.teal--text /geography/countries] will override #[span.teal--text /geography].
- .body-2.pl-3.pt-2 When 2 rules have the same specificity, the priority is given from lowest to highest as follows:
- .body-2.pl-3.pt-1
- ul
- li
- strong Path Starts With...
- em.caption.pl-1 (lowest)
- li
- strong Path Ends With...
- li
- strong Path Matches Regex...
- li
- strong Tag Matches...
- li
- strong Path Is Exactly...
- em.caption.pl-1 (highest)
- .body-2.pl-3.pt-2 When 2 rules have the same path specificity AND the same match type, #[strong.red--text DENY] will always override an #[strong.green--text ALLOW] rule.
- v-divider.mt-3
- .overline.py-3 Regular Expressions
- span Expressions that are deemed unsafe or could result in exponential time processing will be rejected upon saving.
-
-
-
-
-
-
diff --git a/client/components/admin/admin-groups-edit-users.vue b/client/components/admin/admin-groups-edit-users.vue
deleted file mode 100644
index 17368825..00000000
--- a/client/components/admin/admin-groups-edit-users.vue
+++ /dev/null
@@ -1,149 +0,0 @@
-
- v-card(flat)
- v-card-title.pb-4(:class='$vuetify.theme.dark ? `grey darken-3-d3` : `grey lighten-5`')
- v-text-field(
- outlined
- flat
- prepend-inner-icon='mdi-magnify'
- v-model='search'
- label='Search Group Users...'
- hide-details
- dense
- style='max-width: 450px;'
- )
- v-spacer
- v-btn(color='primary', depressed, @click='searchUserDialog = true', :disabled='group.id === 2')
- v-icon(left) mdi-clipboard-account
- | Assign User
- v-data-table(
- :items='group.users',
- :headers='headers',
- :search='search'
- :page.sync='pagination'
- :items-per-page='15'
- @page-count='pageCount = $event'
- must-sort,
- hide-default-footer
- )
- template(v-slot:item.actions='{ item }')
- v-menu(bottom, right, min-width='200')
- template(v-slot:activator='{ on }')
- v-btn(icon, v-on='on', small)
- v-icon.grey--text.text--darken-1 mdi-dots-horizontal
- v-list(dense, nav)
- v-list-item(:to='`/users/` + item.id')
- v-list-item-action: v-icon(color='primary') mdi-account-outline
- v-list-item-content
- v-list-item-title View User Profile
- template(v-if='item.id !== 2')
- v-list-item(@click='unassignUser(item.id)')
- v-list-item-action: v-icon(color='orange') mdi-account-remove-outline
- v-list-item-content
- v-list-item-title Unassign
- template(slot='no-data')
- v-alert.ma-3(icon='mdi-alert', outlined) No users to display.
- .text-center.py-2(v-if='group.users.length > 15')
- v-pagination(v-model='pagination', :length='pageCount')
-
- user-search(v-model='searchUserDialog', @select='assignUser')
-
-
-
diff --git a/client/components/admin/admin-groups-edit.vue b/client/components/admin/admin-groups-edit.vue
deleted file mode 100644
index 4f50c8f6..00000000
--- a/client/components/admin/admin-groups-edit.vue
+++ /dev/null
@@ -1,271 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row wrap)
- v-flex(xs12)
- .admin-header
- img(src='/_assets/svg/icon-social-group.svg', alt='Edit Group', style='width: 80px;')
- .admin-header-title
- .headline.blue--text.text--darken-2 Edit Group
- .subtitle-1.grey--text {{group.name}}
- v-spacer
- v-btn(color='grey', icon, outlined, to='/groups')
- v-icon mdi-arrow-left
- v-dialog(v-model='deleteGroupDialog', max-width='500', v-if='!group.isSystem')
- template(v-slot:activator='{ on }')
- v-btn.ml-3(color='red', icon, outlined, v-on='on')
- v-icon(color='red') mdi-trash-can-outline
- v-card
- .dialog-header.is-red Delete Group?
- v-card-text.pa-4 Are you sure you want to delete group #[strong {{ group.name }}]? All users will be unassigned from this group.
- v-card-actions
- v-spacer
- v-btn(text, @click='deleteGroupDialog = false') Cancel
- v-btn(color='red', dark, @click='deleteGroup') Delete
- v-btn.ml-3(color='success', large, depressed, @click='updateGroup')
- v-icon(left) mdi-check
- span Update Group
- v-card.mt-3
- v-tabs.grad-tabs(v-model='tab', :color='$vuetify.theme.dark ? `blue` : `primary`', fixed-tabs, show-arrows, icons-and-text)
- v-tab(key='settings')
- span Settings
- v-icon mdi-cog-box
- v-tab(key='permissions')
- span Permissions
- v-icon mdi-lock-pattern
- v-tab(key='rules')
- span Page Rules
- v-icon mdi-file-lock
- v-tab(key='users')
- span Users
- v-icon mdi-account-group
-
- v-tab-item(key='settings', :transition='false', :reverse-transition='false')
- v-card(flat)
- template(v-if='group.id <= 2')
- v-card-text
- v-alert.radius-7.mb-0(
- color='orange darken-2'
- :class='$vuetify.theme.dark ? "grey darken-4" : "orange lighten-5"'
- outlined
- :value='true'
- icon='mdi-lock-outline'
- ) This is a system group and its settings cannot be modified.
- v-divider
- v-card-text
- v-text-field(
- outlined
- v-model='group.name'
- label='Group Name'
- hide-details
- prepend-icon='mdi-account-group'
- style='max-width: 600px;'
- :disabled='group.id <= 2'
- )
- template(v-if='group.id !== 2')
- v-divider
- v-card-text
- v-text-field(
- outlined
- v-model='group.redirectOnLogin'
- label='Redirect on Login'
- persistent-hint
- hint='The path / URL where the user will be redirected upon successful login.'
- prepend-icon='mdi-arrow-top-left-thick'
- append-icon='mdi-folder-search'
- @click:append='selectPage'
- style='max-width: 850px;'
- :counter='255'
- )
-
- v-tab-item(key='permissions', :transition='false', :reverse-transition='false')
- group-permissions(v-model='group', @refresh='refresh')
-
- v-tab-item(key='rules', :transition='false', :reverse-transition='false')
- group-rules(v-model='group', @refresh='refresh')
-
- v-tab-item(key='users', :transition='false', :reverse-transition='false')
- group-users(v-model='group', @refresh='refresh')
-
- v-card-chin
- v-spacer
- .caption.grey--text.pr-2 Group ID #[strong {{group.id}}]
-
- page-selector(mode='select', v-model='selectPageModal', :open-handler='selectPageHandle', path='home', :locale='currentLang')
-
-
-
-
-
diff --git a/client/components/admin/admin-groups.vue b/client/components/admin/admin-groups.vue
deleted file mode 100644
index 6f1134cc..00000000
--- a/client/components/admin/admin-groups.vue
+++ /dev/null
@@ -1,170 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-people.svg', alt='Groups', style='width: 80px;')
- .admin-header-title
- .headline.blue--text.text--darken-2.animated.fadeInLeft Groups
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p4s Manage groups and their permissions
- v-spacer
- v-btn.animated.fadeInDown.wait-p3s(icon, outlined, color='grey', href='https://docs.requarks.io/groups', target='_blank')
- v-icon mdi-help-circle
- v-btn.animated.fadeInDown.wait-p2s.mx-3(color='grey', outlined, @click='refresh', icon)
- v-icon mdi-refresh
- v-dialog(v-model='newGroupDialog', max-width='500')
- template(v-slot:activator='{ on }')
- v-btn.animated.fadeInDown(color='primary', depressed, v-on='on', large)
- v-icon(left) mdi-plus
- span New Group
- v-card
- .dialog-header.is-short New Group
- v-card-text.pt-5
- v-text-field.md2(
- outlined
- prepend-icon='mdi-account-group'
- v-model='newGroupName'
- label='Group Name'
- counter='255'
- @keyup.enter='createGroup'
- @keyup.esc='newGroupDialog = false'
- ref='groupNameIpt'
- )
- v-card-chin
- v-spacer
- v-btn(text, @click='newGroupDialog = false') Cancel
- v-btn(color='primary', @click='createGroup') Create
- v-card.mt-3.animated.fadeInUp
- v-data-table(
- :items='groups'
- :headers='headers'
- :search='search'
- :page.sync='pagination'
- :items-per-page='15'
- :loading='loading'
- @page-count='pageCount = $event'
- must-sort,
- hide-default-footer
- )
- template(slot='item', slot-scope='props')
- tr.is-clickable(:active='props.selected', @click='$router.push("/groups/" + props.item.id)')
- td {{ props.item.id }}
- td: strong {{ props.item.name }}
- td {{ props.item.userCount }}
- td {{ props.item.createdAt | moment('calendar') }}
- td {{ props.item.updatedAt | moment('calendar') }}
- td
- v-tooltip(left, v-if='props.item.isSystem')
- template(v-slot:activator='{ on }')
- v-icon(v-on='on') mdi-lock-outline
- span System Group
- template(slot='no-data')
- v-alert.ma-3(icon='mdi-alert', :value='true', outline) No groups to display.
- .text-xs-center.py-2(v-if='pageCount > 1')
- v-pagination(v-model='pagination', :length='pageCount')
-
-
-
-
-
diff --git a/client/components/admin/admin-locale.vue b/client/components/admin/admin-locale.vue
deleted file mode 100644
index 10271942..00000000
--- a/client/components/admin/admin-locale.vue
+++ /dev/null
@@ -1,302 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row, wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-globe-earth.svg', alt='Locale', style='width: 80px;')
- .admin-header-title
- .headline.primary--text.animated.fadeInLeft {{ $t('admin:locale.title') }}
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p4s {{ $t('admin:locale.subtitle') }}
- v-spacer
- v-btn.animated.fadeInDown.wait-p3s(icon, outlined, color='grey', href='https://docs.requarks.io/locales', target='_blank')
- v-icon mdi-help-circle
- v-btn.animated.fadeInDown.ml-3(color='success', depressed, @click='save', large, :loading='loading')
- v-icon(left) mdi-check
- span {{$t('common:actions.apply')}}
- v-form.pt-3
- v-layout(row wrap)
- v-flex(xl6 lg5 xs12)
- v-card.wiki-form.animated.fadeInUp
- v-toolbar(color='primary', dark, dense, flat)
- v-toolbar-title.subtitle-1 {{ $t('admin:locale.settings') }}
- v-card-text
- v-select(
- outlined
- :items='installedLocales'
- prepend-icon='mdi-web'
- v-model='selectedLocale'
- item-value='code'
- item-text='nativeName'
- :label='namespacing ? $t("admin:locale.base.labelWithNS") : $t("admin:locale.base.label")'
- persistent-hint
- :hint='$t("admin:locale.base.hint")'
- )
- template(slot='item', slot-scope='data')
- template(v-if='typeof data.item !== "object"')
- v-list-item-content(v-text='data.item')
- template(v-else)
- v-list-item-avatar
- v-avatar.blue.white--text(tile, size='40', v-html='data.item.code.toUpperCase()')
- v-list-item-content
- v-list-item-title(v-html='data.item.name')
- v-list-item-subtitle(v-html='data.item.nativeName')
- v-divider.mt-3
- v-switch(
- inset
- v-model='autoUpdate'
- :label='$t("admin:locale.autoUpdate.label")'
- color='primary'
- persistent-hint
- :hint='namespacing ? $t("admin:locale.autoUpdate.hintWithNS") : $t("admin:locale.autoUpdate.hint")'
- )
-
- v-card.wiki-form.mt-3.animated.fadeInUp.wait-p2s
- v-toolbar(color='primary', dark, dense, flat)
- v-toolbar-title.subtitle-1 {{ $t('admin:locale.namespacing') }}
- v-card-text
- v-switch(
- inset
- v-model='namespacing'
- :label='$t("admin:locale.namespaces.label")'
- color='primary'
- persistent-hint
- :hint='$t("admin:locale.namespaces.hint")'
- )
- v-alert.mt-3(
- outlined
- color='orange'
- :value='true'
- icon='mdi-alert'
- )
- span {{ $t('admin:locale.namespacingPrefixWarning.title', { langCode: selectedLocale }) }}
- .caption.grey--text {{ $t('admin:locale.namespacingPrefixWarning.subtitle') }}
- v-divider.mt-3.mb-4
- v-select(
- outlined
- :disabled='!namespacing'
- :items='installedLocales'
- prepend-icon='mdi-web'
- multiple
- chips
- deletable-chips
- v-model='namespaces'
- item-value='code'
- item-text='name'
- :label='$t("admin:locale.activeNamespaces.label")'
- persistent-hint
- small-chips
- :hint='$t("admin:locale.activeNamespaces.hint")'
- )
- template(slot='item', slot-scope='data')
- template(v-if='typeof data.item !== "object"')
- v-list-item-content(v-text='data.item')
- template(v-else)
- v-list-item-avatar
- v-avatar.blue.white--text(tile, size='40', v-html='data.item.code.toUpperCase()')
- v-list-item-content
- v-list-item-title(v-html='data.item.name')
- v-list-item-subtitle(v-html='data.item.nativeName')
- v-list-item-action
- v-checkbox(:input-value='data.attrs.inputValue', color='primary', value)
- v-flex(xl6 lg7 xs12)
- v-card.animated.fadeInUp.wait-p4s
- v-toolbar(color='teal', dark, dense, flat)
- v-toolbar-title.subtitle-1 {{ $t('admin:locale.downloadTitle') }}
- v-data-table(
- :headers='headers',
- :items='locales',
- hide-default-footer,
- item-key='code',
- :items-per-page='1000'
- )
- template(v-slot:item.code='{ item }')
- v-chip.white--text(label, color='teal', small) {{item.code}}
- template(v-slot:item.name='{ item }')
- strong {{item.name}}
- template(v-slot:item.isRTL='{ item }')
- v-icon(v-if='item.isRTL') mdi-check
- template(v-slot:item.availability='{ item }')
- .d-flex.align-center.pl-4
- v-progress-circular(:value='item.availability', width='2', size='20', :color='item.availability <= 33 ? `red` : (item.availability <= 66) ? `orange` : `green`')
- .caption.mx-2(:class='item.availability <= 33 ? `red--text` : (item.availability <= 66) ? `orange--text` : `green--text`') {{item.availability}}%
- template(v-slot:item.isInstalled='{ item }')
- v-progress-circular(v-if='item.isDownloading', indeterminate, color='blue', size='20', :width='2')
- v-btn(v-else-if='item.isInstalled && item.installDate < item.updatedAt', icon, small, @click='download(item)')
- v-icon.blue--text mdi-cached
- v-btn(v-else-if='item.isInstalled', icon, small, @click='download(item)')
- v-icon.green--text mdi-check-bold
- v-btn(v-else, icon, small, @click='download(item)')
- v-icon.grey--text mdi-cloud-download
- v-card.wiki-form.mt-3.animated.fadeInUp.wait-p5s
- v-toolbar(color='teal', dark, dense, flat)
- v-toolbar-title.subtitle-1 {{ $t('admin:locale.sideload') }}
- v-spacer
- v-chip(label, color='white', small).teal--text coming soon
- v-card-text
- div {{ $t('admin:locale.sideloadHelp') }}
- v-btn.ml-0.mt-3(color='teal', disabled) {{ $t('common:actions.browse') }}
-
-
-
diff --git a/client/components/admin/admin-logging-console.vue b/client/components/admin/admin-logging-console.vue
deleted file mode 100644
index 5e14553c..00000000
--- a/client/components/admin/admin-logging-console.vue
+++ /dev/null
@@ -1,106 +0,0 @@
-
- v-dialog(v-model='isShown', width='90vw', max-width='1200')
- .dialog-header
- span Live Console
- v-spacer
- .caption.blue--text.text--lighten-3.mr-3 Streaming...
- v-progress-circular(
- indeterminate
- color='blue lighten-3'
- :size='20'
- :width='2'
- )
- .consoleTerm(ref='consoleContainer')
- v-toolbar(flat, color='grey darken-3', dark)
- v-spacer
- v-btn(outline, @click='clear')
- v-icon(left) cancel_presentation
- span Clear
- v-btn(outline, @click='close')
- v-icon(left) close
- span Close
-
-
-
-
-
diff --git a/client/components/admin/admin-logging.vue b/client/components/admin/admin-logging.vue
deleted file mode 100644
index 25d157e5..00000000
--- a/client/components/admin/admin-logging.vue
+++ /dev/null
@@ -1,194 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row, wrap)
- v-flex(xs12)
- .admin-header
- img(src='/_assets/svg/icon-registry-editor.svg', alt='Logging', style='width: 80px;')
- .admin-header-title
- .headline.primary--text Logging
- .subtitle-1.grey--text Configure the system logger(s) #[v-chip(label, color='primary', small).white--text coming soon]
- v-spacer
- v-btn(outline, color='grey', @click='refresh', large)
- v-icon refresh
- v-btn(color='black', disabled, depressed, @click='toggleConsole', large)
- v-icon check
- span Live Trail
- v-btn(color='success', @click='save', depressed, large)
- v-icon(left) check
- span {{$t('common:actions.apply')}}
-
- v-card.mt-3
- v-tabs(color='grey darken-2', fixed-tabs, slider-color='white', show-arrows, dark)
- v-tab(key='settings'): v-icon settings
- v-tab(v-for='logger in activeLoggers', :key='logger.key') {{ logger.title }}
-
- v-tab-item(key='settings', :transition='false', :reverse-transition='false')
- v-card.pa-3(flat, tile)
- .body-2.grey--text.text--darken-1 Select which logging service to enable:
- .caption.grey--text.pb-2 Some loggers require additional configuration in their dedicated tab (when selected).
- v-form
- v-checkbox.my-0(
- v-for='(logger, n) in loggers'
- v-model='logger.isEnabled'
- :key='logger.key'
- :label='logger.title'
- color='primary'
- hide-details
- disabled
- )
-
- v-tab-item(v-for='(logger, n) in activeLoggers', :key='logger.key', :transition='false', :reverse-transition='false')
- v-card.wiki-form.pa-3(flat, tile)
- v-form
- .loggerlogo
- img(:src='logger.logo', :alt='logger.title')
- v-subheader.pl-0 {{logger.title}}
- .caption {{logger.description}}
- .caption: a(:href='logger.website') {{logger.website}}
- v-divider.mt-3
- v-subheader.pl-0 Logger Configuration
- .body-1.ml-3(v-if='!logger.config || logger.config.length < 1') This logger has no configuration options you can modify.
- template(v-else, v-for='cfg in logger.config')
- v-select(
- v-if='cfg.value.type === "string" && cfg.value.enum'
- outline
- background-color='grey lighten-2'
- :items='cfg.value.enum'
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- prepend-icon='settings_applications'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- :class='cfg.value.hint ? "mb-2" : ""'
- )
- v-switch(
- v-else-if='cfg.value.type === "boolean"'
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- color='primary'
- prepend-icon='settings_applications'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- )
- v-text-field(
- v-else
- outline
- background-color='grey lighten-2'
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- prepend-icon='settings_applications'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- :class='cfg.value.hint ? "mb-2" : ""'
- )
- v-divider.mt-3
- v-subheader.pl-0 Log Level
- .body-1.ml-3 Select the minimum error level that will be reported to this logger.
- v-layout(row)
- v-flex(xs12, md6, lg4)
- .pt-3
- v-select(
- single-line
- outline
- background-color='grey lighten-2'
- :items='levels'
- label='Level'
- v-model='logger.level'
- prepend-icon='graphic_eq'
- hint='Default: warn'
- persistent-hint
- )
-
- logging-console(v-model='showConsole')
-
-
-
-
-
diff --git a/client/components/admin/admin-mail.vue b/client/components/admin/admin-mail.vue
deleted file mode 100644
index 660660f9..00000000
--- a/client/components/admin/admin-mail.vue
+++ /dev/null
@@ -1,258 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row, wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-new-post.svg', alt='Mail', style='width: 80px;')
- .admin-header-title
- .headline.primary--text.animated.fadeInLeft {{ $t('admin:mail.title') }}
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p4s {{ $t('admin:mail.subtitle') }}
- v-spacer
- v-btn.animated.fadeInDown(color='success', depressed, @click='save', large)
- v-icon(left) mdi-check
- span {{$t('common:actions.apply')}}
- v-form.pt-3
- v-layout(row wrap)
- v-flex(lg6 xs12)
- v-form
- v-card.animated.fadeInUp
- v-toolbar(color='primary', dark, dense, flat)
- v-toolbar-title.subtitle-1 {{ $t('admin:mail.configuration') }}
- .overline.pa-4.grey--text {{ $t('admin:mail.sender') }}
- .px-4
- v-text-field(
- outlined
- v-model='config.senderName'
- :label='$t(`admin:mail.senderName`)'
- required
- :counter='255'
- prepend-icon='mdi-mailbox'
- )
- v-text-field(
- outlined
- v-model='config.senderEmail'
- :label='$t(`admin:mail.senderEmail`)'
- required
- :counter='255'
- prepend-icon='mdi-mailbox'
- )
- v-divider
- .overline.pa-4.grey--text {{ $t('admin:mail.smtp') }}
- .px-4
- v-text-field(
- outlined
- v-model='config.host'
- :label='$t(`admin:mail.smtpHost`)'
- required
- :counter='255'
- prepend-icon='mdi-memory'
- )
- v-text-field(
- outlined
- v-model='config.port'
- :label='$t(`admin:mail.smtpPort`)'
- required
- prepend-icon='mdi-serial-port'
- persistent-hint
- :hint='$t(`admin:mail.smtpPortHint`)'
- style='max-width: 300px;'
- )
- v-switch(
- v-model='config.secure'
- :label='$t(`admin:mail.smtpTLS`)'
- color='primary'
- persistent-hint
- :hint='$t(`admin:mail.smtpTLSHint`)'
- prepend-icon='mdi-security-network'
- inset
- )
- v-switch(
- v-model='config.verifySSL'
- :label='$t(`admin:mail.smtpVerifySSL`)'
- color='primary'
- persistent-hint
- :hint='$t(`admin:mail.smtpVerifySSLHint`)'
- prepend-icon='mdi-security-network'
- inset
- )
- v-text-field.mt-8(
- outlined
- v-model='config.user'
- :label='$t(`admin:mail.smtpUser`)'
- required
- :counter='255'
- prepend-icon='mdi-shield-account-outline'
- )
- v-text-field(
- outlined
- v-model='config.pass'
- :label='$t(`admin:mail.smtpPwd`)'
- required
- prepend-icon='mdi-form-textbox-password'
- type='password'
- )
-
- v-flex(lg6 xs12)
- v-card.animated.fadeInUp.wait-p2s
- v-form
- v-toolbar(color='primary', dark, dense, flat)
- v-toolbar-title.subtitle-1 {{ $t('admin:mail.dkim') }}
- v-card-info
- span {{ $t('admin:mail.dkimHint') }}
- .pa-4
- v-switch(
- v-model='config.useDKIM'
- :label='$t(`admin:mail.dkimUse`)'
- color='primary'
- prepend-icon='mdi-key'
- inset
- )
- v-text-field(
- outlined
- v-model='config.dkimDomainName'
- :label='$t(`admin:mail.dkimDomainName`)'
- :counter='255'
- prepend-icon='mdi-key'
- :disabled='!config.useDKIM'
- )
- v-text-field(
- outlined
- v-model='config.dkimKeySelector'
- :label='$t(`admin:mail.dkimKeySelector`)'
- :counter='255'
- prepend-icon='mdi-key'
- :disabled='!config.useDKIM'
- )
- v-textarea(
- outlined
- v-model='config.dkimPrivateKey'
- :label='$t(`admin:mail.dkimPrivateKey`)'
- prepend-icon='mdi-key'
- persistent-hint
- :hint='$t(`admin:mail.dkimPrivateKeyHint`)'
- :disabled='!config.useDKIM'
- )
-
- v-card.mt-3.animated.fadeInUp.wait-p3s
- v-form
- v-toolbar(color='teal', dark, dense, flat)
- v-toolbar-title.subtitle-1 {{ $t('admin:mail.test') }}
- .pa-4
- .body-2.grey--text.text--darken-2 {{ $t('admin:mail.testHint') }}
- v-text-field.mt-3(
- outlined
- v-model='testEmail'
- :label='$t(`admin:mail.testRecipient`)'
- :counter='255'
- prepend-icon='mdi-email-outline'
- :disabled='testLoading'
- )
- v-card-chin
- v-spacer
- v-btn.px-4(color='teal', dark, @click='sendTest', :loading='testLoading')
- v-icon(left) mdi-send
- span {{ $t('admin:mail.testSend') }}
-
-
-
-
-
-
diff --git a/client/components/admin/admin-navigation.vue b/client/components/admin/admin-navigation.vue
deleted file mode 100644
index e4796c2f..00000000
--- a/client/components/admin/admin-navigation.vue
+++ /dev/null
@@ -1,526 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-triangle-arrow.svg', alt='Navigation', style='width: 80px;')
- .admin-header-title
- .headline.primary--text.animated.fadeInLeft {{$t('navigation.title')}}
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p4s {{$t('navigation.subtitle')}}
- v-spacer
- v-btn.animated.fadeInDown.wait-p3s(icon, outlined, color='grey', href='https://docs.requarks.io/navigation', target='_blank')
- v-icon mdi-help-circle
- v-btn.mx-3.animated.fadeInDown.wait-p2s.mr-3(icon, outlined, color='grey', @click='refresh')
- v-icon mdi-refresh
- v-btn.animated.fadeInDown(color='success', depressed, @click='save', large)
- v-icon(left) mdi-check
- span {{$t('common:actions.apply')}}
- v-container.pa-0.mt-3(fluid, grid-list-lg)
- v-row(dense)
- v-col(cols='3')
- v-card.animated.fadeInUp
- v-toolbar(color='teal', dark, dense, flat, height='56')
- v-toolbar-title.subtitle-1 {{$t('admin:navigation.mode')}}
- v-list(nav, two-line)
- v-list-item-group(v-model='config.mode', mandatory, :color='$vuetify.theme.dark ? `teal lighten-3` : `teal`')
- v-list-item(value='TREE')
- v-list-item-avatar
- img(src='/_assets/svg/icon-tree-structure-dotted.svg', alt='Site Tree')
- v-list-item-content
- v-list-item-title {{$t('admin:navigation.modeSiteTree.title')}}
- v-list-item-subtitle {{$t('admin:navigation.modeSiteTree.description')}}
- v-list-item-avatar
- v-icon(v-if='$vuetify.theme.dark', :color='config.mode === `TREE` ? `teal lighten-3` : `grey darken-2`') mdi-check-circle
- v-icon(v-else, :color='config.mode === `TREE` ? `teal` : `grey lighten-3`') mdi-check-circle
- v-list-item(value='STATIC')
- v-list-item-avatar
- img(src='/_assets/svg/icon-features-list.svg', alt='Static Navigation')
- v-list-item-content
- v-list-item-title {{$t('admin:navigation.modeStatic.title')}}
- v-list-item-subtitle {{$t('admin:navigation.modeStatic.description')}}
- v-list-item-avatar
- v-icon(v-if='$vuetify.theme.dark', :color='config.mode === `STATIC` ? `teal lighten-3` : `grey darken-2`') mdi-check-circle
- v-icon(v-else, :color='config.mode === `STATIC` ? `teal` : `grey lighten-3`') mdi-check-circle
- v-list-item(value='MIXED')
- v-list-item-avatar
- img(src='/_assets/svg/icon-user-menu-male-dotted.svg', alt='Custom Navigation')
- v-list-item-content
- v-list-item-title {{$t('admin:navigation.modeCustom.title')}}
- v-list-item-subtitle {{$t('admin:navigation.modeCustom.description')}}
- v-list-item-avatar
- v-icon(v-if='$vuetify.theme.dark', :color='config.mode === `MIXED` ? `teal lighten-3` : `grey darken-2`') mdi-check-circle
- v-icon(v-else, :color='config.mode === `MIXED` ? `teal` : `grey lighten-3`') mdi-check-circle
- v-list-item(value='NONE')
- v-list-item-avatar
- img(src='/_assets/svg/icon-cancel-dotted.svg', alt='None')
- v-list-item-content
- v-list-item-title {{$t('admin:navigation.modeNone.title')}}
- v-list-item-subtitle {{$t('admin:navigation.modeNone.description')}}
- v-list-item-avatar
- v-icon(v-if='$vuetify.theme.dark', :color='config.mode === `none` ? `teal lighten-3` : `grey darken-2`') mdi-check-circle
- v-icon(v-else, :color='config.mode === `none` ? `teal` : `grey lighten-3`') mdi-check-circle
- v-col(cols='9', v-if='config.mode === `MIXED` || config.mode === `STATIC`')
- v-card.animated.fadeInUp.wait-p2s
- v-row(no-gutters, align='stretch')
- v-col(style='flex: 0 0 350px;')
- v-card.grey(flat, style='height: 100%; border-radius: 4px 0 0 4px;', :class='$vuetify.theme.dark ? `darken-4-l5` : `lighten-3`')
- .teal.lighten-1.pa-2.d-flex(style='margin-bottom: 1px; height:56px;')
- v-select(
- :disabled='locales.length < 2'
- label='Locale'
- hide-details
- solo
- flat
- background-color='teal darken-2'
- dark
- dense
- v-model='currentLang'
- :items='locales'
- item-text='nativeName'
- item-value='code'
- )
- v-tooltip(top)
- template(v-slot:activator='{ on }')
- v-btn.ml-2(icon, tile, color='white', v-on='on', @click='copyFromLocaleDialogIsShown = true')
- v-icon mdi-arrange-send-backward
- span {{$t('admin:navigation.copyFromLocale')}}
- v-list.py-2(dense, nav, dark, class='blue darken-2', style='border-radius: 0;')
- v-list-item(v-if='currentTree.length < 1')
- v-list-item-avatar(size='24'): v-icon(color='blue lighten-3') mdi-alert
- v-list-item-content
- em.caption.blue--text.text--lighten-4 {{$t('navigation.emptyList')}}
- draggable(v-model='currentTree')
- template(v-for='navItem in currentTree')
- v-list-item(
- v-if='navItem.kind === "link"'
- :key='navItem.id'
- :class='(navItem === current) ? "blue" : ""'
- @click='selectItem(navItem)'
- )
- v-list-item-avatar(size='24', tile)
- v-icon(v-if='navItem.icon.match(/fa[a-z] fa-/)', size='19') {{ navItem.icon }}
- v-icon(v-else) {{ navItem.icon }}
- v-list-item-title {{navItem.label}}
- .py-2.clickable(
- v-else-if='navItem.kind === "divider"'
- :key='navItem.id'
- :class='(navItem === current) ? "blue" : ""'
- @click='selectItem(navItem)'
- )
- v-divider
- v-subheader.pl-4.clickable(
- v-else-if='navItem.kind === "header"'
- :key='navItem.id'
- :class='(navItem === current) ? "blue" : ""'
- @click='selectItem(navItem)'
- ) {{navItem.label}}
- v-card-chin
- v-menu(offset-y, bottom, min-width='200px', style='flex: 1 1;')
- template(v-slot:activator='{ on }')
- v-btn(v-on='on', color='primary', depressed, block)
- v-icon(left) mdi-plus
- span {{$t('common:actions.add')}}
- v-list
- v-list-item(@click='addItem("link")')
- v-list-item-avatar(size='24'): v-icon mdi-link
- v-list-item-title {{$t('navigation.link')}}
- v-list-item(@click='addItem("header")')
- v-list-item-avatar(size='24'): v-icon mdi-format-title
- v-list-item-title {{$t('navigation.header')}}
- v-list-item(@click='addItem("divider")')
- v-list-item-avatar(size='24'): v-icon mdi-minus
- v-list-item-title {{$t('navigation.divider')}}
- v-col
- v-card(flat, style='border-radius: 0 4px 4px 0;')
- template(v-if='current.kind === "link"')
- v-toolbar(height='56', color='teal lighten-1', flat, dark)
- .subtitle-1 {{$t('navigation.edit', { kind: $t('navigation.link') })}}
- v-spacer
- v-btn.px-5(color='white', outlined, @click='deleteItem(current)')
- v-icon(left) mdi-delete
- span {{$t('navigation.delete', { kind: $t('navigation.link') })}}
- v-card-text
- v-text-field(
- outlined
- :label='$t("navigation.label")'
- prepend-icon='mdi-format-title'
- v-model='current.label'
- counter='255'
- )
- v-text-field(
- outlined
- :label='$t("navigation.icon")'
- prepend-icon='mdi-dice-5'
- v-model='current.icon'
- hide-details
- )
- .caption.pt-3.pl-5 The default icon set is #[strong Material Design Icons]. In order to use another icon set, you must first select it in the Theme administration section.
- .caption.pt-3.pl-5: strong Material Design Icons
- .caption.pl-5 Refer to the #[a(href='https://materialdesignicons.com/', target='_blank') Material Design Icons Reference] for the list of all possible values. You must prefix all values with #[code mdi-], e.g. #[code mdi-home]
- .caption.pt-3.pl-5: strong Font Awesome 5
- .caption.pl-5 Refer to the #[a(href='https://fontawesome.com/icons?d=gallery&m=free', target='_blank') Font Awesome 5 Reference] for the list of all possible values. You must prefix all values with #[code fas fa-], e.g. #[code fas fa-home]. Note that some icons use different prefixes (e.g. #[code fab], #[code fad], #[code fal], #[code far]).
- .caption.pt-3.pl-5: strong Font Awesome 4
- .caption.pl-5 Refer to the #[a(href='https://fontawesome.com/v4.7.0/icons/', target='_blank') Font Awesome 4 Reference] for the list of all possible values. You must prefix all values with #[code fa fa-], e.g. #[code fa fa-home]
- v-divider
- v-card-text
- v-select(
- outlined
- :label='$t("navigation.targetType")'
- prepend-icon='mdi-near-me'
- :items='navTypes'
- v-model='current.targetType'
- hide-details
- )
- v-text-field.mt-4(
- v-if='current.targetType === `external` || current.targetType === `externalblank`'
- outlined
- :label='$t("navigation.target")'
- prepend-icon='mdi-near-me'
- v-model='current.target'
- hide-details
- )
- .d-flex.align-center.mt-4(v-else-if='current.targetType === "page"')
- v-btn.ml-8(
- color='primary'
- dark
- @click='selectPage'
- )
- v-icon(left) mdi-magnify
- span {{$t('admin:navigation.selectPageButton')}}
- .caption.ml-4.primary--text {{current.target}}
- v-text-field(
- v-else-if='current.targetType === `search`'
- outlined
- :label='$t("navigation.navType.searchQuery")'
- prepend-icon='search'
- v-model='current.target'
- )
- v-divider
-
- template(v-else-if='current.kind === "header"')
- v-toolbar(height='56', color='teal lighten-1', flat, dark)
- .subtitle-1 {{$t('navigation.edit', { kind: $t('navigation.header') })}}
- v-spacer
- v-btn.px-5(color='white', outlined, @click='deleteItem(current)')
- v-icon(left) mdi-delete
- span {{$t('navigation.delete', { kind: $t('navigation.header') })}}
- v-card-text
- v-text-field(
- outlined
- :label='$t("navigation.label")'
- prepend-icon='mdi-format-title'
- v-model='current.label'
- )
- v-divider
-
- div(v-else-if='current.kind === "divider"')
- v-toolbar(height='56', color='teal lighten-1', flat, dark)
- .subtitle-1 {{$t('navigation.edit', { kind: $t('navigation.divider') })}}
- v-spacer
- v-btn.px-5(color='white', outlined, @click='deleteItem(current)')
- v-icon(left) mdi-delete
- span {{$t('navigation.delete', { kind: $t('navigation.divider') })}}
-
- v-card-text(v-if='current.kind')
- v-radio-group.pl-8(v-model='current.visibilityMode', mandatory, hide-details)
- v-radio(:label='$t("admin:navigation.visibilityMode.all")', value='all', color='primary')
- v-radio.mt-3(:label='$t("admin:navigation.visibilityMode.restricted")', value='restricted', color='primary')
- .pl-8
- v-select.pl-8.mt-3(
- item-text='name'
- item-value='id'
- outlined
- prepend-icon='mdi-account-group'
- label='Groups'
- :disabled='current.visibilityMode !== `restricted`'
- v-model='current.visibilityGroups'
- :items='groups'
- persistent-hint
- clearable
- multiple
- )
- template(v-else)
- v-toolbar(height='56', color='teal lighten-1', flat, dark)
- v-card-text.grey--text(v-if='currentTree.length > 0') {{$t('navigation.noSelectionText')}}
- v-card-text.grey--text(v-else) {{$t('navigation.noItemsText')}}
-
- v-dialog(v-model='copyFromLocaleDialogIsShown', max-width='650', persistent)
- v-card
- .dialog-header.is-short.is-teal
- v-icon.mr-3(color='white') mdi-arrange-send-backward
- span {{$t('admin:navigation.copyFromLocale')}}
- v-card-text.pt-5
- .body-2 {{$t('admin:navigation.copyFromLocaleInfoText')}}
- v-select.mt-3(
- :items='locales'
- item-text='nativeName'
- item-value='code'
- outlined
- prepend-icon='mdi-web'
- v-model='copyFromLocaleCode'
- :label='$t(`admin:navigation.sourceLocale`)'
- :hint='$t(`admin:navigation.sourceLocaleHint`)'
- persistent-hint
- )
- v-card-chin
- v-spacer
- v-btn(text, @click='copyFromLocaleDialogIsShown = false') {{$t('common:actions.cancel')}}
- v-btn.px-3(depressed, color='primary', @click='copyFromLocale')
- v-icon(left) mdi-chevron-right
- span {{$t('common:actions.copy')}}
-
- page-selector(mode='select', v-model='selectPageModal', :open-handler='selectPageHandle', path='home', :locale='currentLang')
-
-
-
-
-
diff --git a/client/components/admin/admin-pages-edit.vue b/client/components/admin/admin-pages-edit.vue
deleted file mode 100644
index cd43533b..00000000
--- a/client/components/admin/admin-pages-edit.vue
+++ /dev/null
@@ -1,235 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row, wrap, v-if='page.id')
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-view-details.svg', alt='Edit Page', style='width: 80px;')
- .admin-header-title
- .headline.blue--text.text--darken-2.animated.fadeInLeft Page Details
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s
- v-chip.ml-0.mr-2(label, small).caption ID {{page.id}}
- span /{{page.locale}}/{{page.path}}
- v-spacer
- template(v-if='page.isPublished')
- status-indicator.mr-3(positive, pulse)
- .caption.green--text {{$t('common:page.published')}}
- template(v-else)
- status-indicator.mr-3(negative, pulse)
- .caption.red--text {{$t('common:page.unpublished')}}
- template(v-if='page.isPrivate')
- status-indicator.mr-3.ml-4(intermediary, pulse)
- .caption.deep-orange--text {{$t('common:page.private')}}
- template(v-else)
- status-indicator.mr-3.ml-4(active, pulse)
- .caption.blue--text {{$t('common:page.global')}}
- v-spacer
- v-btn.animated.fadeInDown.wait-p3s(color='grey', icon, outlined, to='/pages')
- v-icon mdi-arrow-left
- v-menu(offset-y, origin='top right')
- template(v-slot:activator='{ on }')
- v-btn.mx-3.animated.fadeInDown.wait-p2s(color='black', v-on='on', depressed, dark)
- span Actions
- v-icon(right) mdi-chevron-down
- v-list(dense, nav)
- v-list-item(:href='`/` + page.locale + `/` + page.path')
- v-list-item-icon
- v-icon(color='indigo') mdi-text-subject
- v-list-item-title View
- v-list-item(:href='`/e/` + page.locale + `/` + page.path')
- v-list-item-icon
- v-icon(color='indigo') mdi-pencil
- v-list-item-title Edit
- v-list-item(@click='', disabled)
- v-list-item-icon
- v-icon(color='grey') mdi-cube-scan
- v-list-item-title Re-Render
- v-list-item(@click='', disabled)
- v-list-item-icon
- v-icon(color='grey') mdi-earth-remove
- v-list-item-title Unpublish
- v-list-item(:href='`/s/` + page.locale + `/` + page.path')
- v-list-item-icon
- v-icon(color='indigo') mdi-code-tags
- v-list-item-title View Source
- v-list-item(:href='`/h/` + page.locale + `/` + page.path')
- v-list-item-icon
- v-icon(color='indigo') mdi-history
- v-list-item-title View History
- v-list-item(@click='', disabled)
- v-list-item-icon
- v-icon(color='grey') mdi-content-duplicate
- v-list-item-title Duplicate
- v-list-item(@click='', disabled)
- v-list-item-icon
- v-icon(color='grey') mdi-content-save-move-outline
- v-list-item-title Move / Rename
- v-dialog(v-model='deletePageDialog', max-width='500')
- template(v-slot:activator='{ on }')
- v-list-item(v-on='on')
- v-list-item-icon
- v-icon(color='red') mdi-trash-can-outline
- v-list-item-title Delete
- v-card
- .dialog-header.is-short.is-red
- v-icon.mr-2(color='white') mdi-file-document-box-remove-outline
- span {{$t('common:page.delete')}}
- v-card-text.pt-5
- i18next.body-2(path='common:page.deleteTitle', tag='div')
- span.red--text.text--darken-2(place='title') {{page.title}}
- .caption {{$t('common:page.deleteSubtitle')}}
- v-chip.mt-3.ml-0.mr-1(label, color='red lighten-4', disabled, small)
- .caption.red--text.text--darken-2 {{page.locale.toUpperCase()}}
- v-chip.mt-3.mx-0(label, color='red lighten-5', disabled, small)
- span.red--text.text--darken-2 /{{page.path}}
- v-card-chin
- v-spacer
- v-btn(text, @click='deletePageDialog = false', :disabled='loading') {{$t('common:actions.cancel')}}
- v-btn(color='red darken-2', @click='deletePage', :loading='loading').white--text {{$t('common:actions.delete')}}
- v-btn.animated.fadeInDown(color='success', large, depressed, disabled)
- v-icon(left) mdi-check
- span Save Changes
- v-flex(xs12, lg6)
- v-card.animated.fadeInUp
- v-toolbar(color='primary', dense, dark, flat)
- v-icon.mr-2 mdi-text-subject
- span Properties
- v-list.py-0(two-line, dense)
- v-list-item
- v-list-item-content
- v-list-item-title: .overline.grey--text Title
- v-list-item-subtitle.body-2(:class='$vuetify.theme.dark ? `grey--text text--lighten-2` : `grey--text text--darken-3`') {{ page.title }}
- v-divider
- v-list-item
- v-list-item-content
- v-list-item-title: .overline.grey--text Description
- v-list-item-subtitle.body-2(:class='$vuetify.theme.dark ? `grey--text text--lighten-2` : `grey--text text--darken-3`') {{ page.description || '-' }}
- v-divider
- v-list-item
- v-list-item-content
- v-list-item-title: .overline.grey--text Locale
- v-list-item-subtitle.body-2(:class='$vuetify.theme.dark ? `grey--text text--lighten-2` : `grey--text text--darken-3`') {{ page.locale }}
- v-divider
- v-list-item
- v-list-item-content
- v-list-item-title: .overline.grey--text Path
- v-list-item-subtitle.body-2(:class='$vuetify.theme.dark ? `grey--text text--lighten-2` : `grey--text text--darken-3`') {{ page.path }}
- v-divider
- v-list-item
- v-list-item-content
- v-list-item-title: .overline.grey--text Editor
- v-list-item-subtitle.body-2(:class='$vuetify.theme.dark ? `grey--text text--lighten-2` : `grey--text text--darken-3`') {{ page.editor || '?' }}
- v-divider
- v-list-item
- v-list-item-content
- v-list-item-title: .overline.grey--text Content Type
- v-list-item-subtitle.body-2(:class='$vuetify.theme.dark ? `grey--text text--lighten-2` : `grey--text text--darken-3`') {{ page.contentType || '?' }}
- v-divider
- v-list-item
- v-list-item-content
- v-list-item-title: .overline.grey--text Page Hash
- v-list-item-subtitle.body-2(:class='$vuetify.theme.dark ? `grey--text text--lighten-2` : `grey--text text--darken-3`') {{ page.hash }}
-
- v-flex(xs12, lg6)
- v-card.animated.fadeInUp.wait-p2s
- v-toolbar(color='primary', dense, dark, flat)
- v-icon.mr-2 mdi-account-multiple
- span Users
- v-list.py-0(two-line, dense)
- v-list-item
- v-list-item-avatar(size='24')
- v-btn(icon, :to='`/users/` + page.creatorId')
- v-icon(color='grey') mdi-account
- v-list-item-content
- v-list-item-title: .overline.grey--text Creator
- v-list-item-subtitle.body-2(:class='$vuetify.theme.dark ? `grey--text text--lighten-2` : `grey--text text--darken-3`') {{ page.creatorName }} #[em.caption ({{ page.creatorEmail }})]
- v-list-item-action
- v-list-item-action-text {{ page.createdAt | moment('calendar') }}
- v-divider
- v-list-item
- v-list-item-avatar(size='24')
- v-btn(icon, :to='`/users/` + page.authorId')
- v-icon(color='grey') mdi-account
- v-list-item-content
- v-list-item-title: .overline.grey--text Last Editor
- v-list-item-subtitle.body-2(:class='$vuetify.theme.dark ? `grey--text text--lighten-2` : `grey--text text--darken-3`') {{ page.authorName }} #[em.caption ({{ page.authorEmail }})]
- v-list-item-action
- v-list-item-action-text {{ page.updatedAt | moment('calendar') }}
-
- v-layout(row, align-center, v-else)
- v-progress-circular(indeterminate, width='2', color='grey')
- .body-2.pl-3.grey--text {{ $t('common:page.loading') }}
-
-
-
-
-
diff --git a/client/components/admin/admin-pages-visualize.vue b/client/components/admin/admin-pages-visualize.vue
deleted file mode 100644
index 116b38a9..00000000
--- a/client/components/admin/admin-pages-visualize.vue
+++ /dev/null
@@ -1,405 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-venn-diagram.svg', alt='Visualize Pages', style='width: 80px;')
- .admin-header-title
- .headline.blue--text.text--darken-2.animated.fadeInLeft Visualize Pages
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s Dendrogram representation of your pages
- v-spacer
- v-select.mx-5.animated.fadeInDown.wait-p1s(
- v-if='locales.length > 0'
- v-model='currentLocale'
- :items='locales'
- style='flex: 0 1 120px;'
- solo
- dense
- hide-details
- item-value='code'
- item-text='name'
- )
- v-btn-toggle.animated.fadeInDown(v-model='graphMode', color='primary', dense, rounded)
- v-btn.px-5(value='htree')
- v-icon(left, :color='graphMode === `htree` ? `primary` : `grey darken-3`') mdi-sitemap
- span.text-none Hierarchical Tree
- v-btn.px-5(value='hradial')
- v-icon(left, :color='graphMode === `hradial` ? `primary` : `grey darken-3`') mdi-chart-donut-variant
- span.text-none Hierarchical Radial
- v-btn.px-5(value='rradial')
- v-icon(left, :color='graphMode === `rradial` ? `primary` : `grey darken-3`') mdi-blur-radial
- span.text-none Relational Radial
- .admin-pages-visualize-svg(ref='svgContainer', v-show='pages.length >= 1')
- v-alert(v-if='pages.length < 1', outlined, type='warning', style='max-width: 650px; margin: 0 auto;') Looks like there's no data yet to graph!
-
-
-
-
-
diff --git a/client/components/admin/admin-pages.vue b/client/components/admin/admin-pages.vue
deleted file mode 100644
index 0ca26de1..00000000
--- a/client/components/admin/admin-pages.vue
+++ /dev/null
@@ -1,169 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-file.svg', alt='Page', style='width: 80px;')
- .admin-header-title
- .headline.blue--text.text--darken-2.animated.fadeInLeft Pages
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s Manage pages
- v-spacer
- v-btn.animated.fadeInDown.wait-p1s(icon, color='grey', outlined, @click='refresh')
- v-icon.grey--text mdi-refresh
- v-btn.animated.fadeInDown.mx-3(color='primary', outlined, @click='recyclebin', disabled)
- v-icon(left) mdi-delete-outline
- span Recycle Bin
- v-btn.animated.fadeInDown(color='primary', depressed, large, to='pages/visualize')
- v-icon(left) mdi-graph
- span Visualize
- v-card.mt-3.animated.fadeInUp
- .pa-2.d-flex.align-center(:class='$vuetify.theme.dark ? `grey darken-3-d5` : `grey lighten-3`')
- v-text-field(
- solo
- flat
- v-model='search'
- prepend-inner-icon='mdi-file-search-outline'
- label='Search Pages...'
- hide-details
- dense
- style='max-width: 400px;'
- )
- v-spacer
- v-select.ml-2(
- solo
- flat
- hide-details
- dense
- label='Locale'
- :items='langs'
- v-model='selectedLang'
- style='max-width: 250px;'
- )
- v-select.ml-2(
- solo
- flat
- hide-details
- dense
- label='Publish State'
- :items='states'
- v-model='selectedState'
- style='max-width: 250px;'
- )
- v-divider
- v-data-table(
- :items='filteredPages'
- :headers='headers'
- :search='search'
- :page.sync='pagination'
- :items-per-page='15'
- :loading='loading'
- must-sort,
- sort-by='updatedAt',
- sort-desc,
- hide-default-footer
- @page-count="pageTotal = $event"
- )
- template(slot='item', slot-scope='props')
- tr.is-clickable(:active='props.selected', @click='$router.push(`/pages/` + props.item.id)')
- td.text-xs-right {{ props.item.id }}
- td
- .body-2: strong {{ props.item.title }}
- .caption {{ props.item.description }}
- td.admin-pages-path
- v-chip(label, small, :color='$vuetify.theme.dark ? `grey darken-4` : `grey lighten-4`') {{ props.item.locale }}
- span.ml-2.grey--text(:class='$vuetify.theme.dark ? `text--lighten-1` : `text--darken-2`') / {{ props.item.path }}
- td {{ props.item.createdAt | moment('calendar') }}
- td {{ props.item.updatedAt | moment('calendar') }}
- template(slot='no-data')
- v-alert.ma-3(icon='mdi-alert', :value='true', outlined) No pages to display.
- .text-center.py-2.animated.fadeInDown(v-if='this.pageTotal > 1')
- v-pagination(v-model='pagination', :length='pageTotal')
-
-
-
-
-
diff --git a/client/components/admin/admin-rendering.vue b/client/components/admin/admin-rendering.vue
deleted file mode 100644
index 5a77cd3c..00000000
--- a/client/components/admin/admin-rendering.vue
+++ /dev/null
@@ -1,261 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row, wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-process.svg', alt='Rendering', style='width: 80px;')
- .admin-header-title
- .headline.primary--text.animated.fadeInLeft {{ $t('admin:rendering.title') }}
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p4s {{ $t('admin:rendering.subtitle') }}
- v-spacer
- v-btn.animated.fadeInDown.wait-p3s(icon, outlined, color='grey', href='https://docs.requarks.io/rendering', target='_blank')
- v-icon mdi-help-circle
- v-btn.mx-3.animated.fadeInDown.wait-p2s(icon, outlined, color='grey', @click='refresh')
- v-icon mdi-refresh
- v-btn.animated.fadeInDown(color='success', @click='save', depressed, large)
- v-icon(left) mdi-check
- span {{$t('common:actions.apply')}}
-
- v-flex.animated.fadeInUp(lg3, xs12)
- v-toolbar(
- color='blue darken-2'
- dense
- flat
- dark
- )
- .subtitle-1 Pipeline
- v-expansion-panels.adm-rendering-pipeline(
- v-model='selectedCore'
- accordion
- mandatory
- )
- v-expansion-panel(
- v-for='core in renderers'
- :key='core.key'
- )
- v-expansion-panel-header(
- hide-actions
- ripple
- )
- v-toolbar(
- color='blue'
- dense
- dark
- flat
- )
- v-spacer
- .body-2 {{core.input}}
- v-icon.mx-2 mdi-arrow-right-circle
- .caption {{core.output}}
- v-spacer
- v-expansion-panel-content
- v-list.py-0(two-line, dense)
- template(v-for='(rdr, n) in core.children')
- v-list-item(
- :key='rdr.key'
- @click='selectRenderer(rdr.key)'
- :class='currentRenderer.key === rdr.key ? ($vuetify.theme.dark ? `grey darken-4-l4` : `blue lighten-5`) : ``'
- )
- v-list-item-avatar(size='24', tile)
- v-icon(:color='currentRenderer.key === rdr.key ? "primary" : "grey"') {{rdr.icon}}
- v-list-item-content
- v-list-item-title {{rdr.title}}
- v-list-item-subtitle: .caption {{rdr.description}}
- v-list-item-avatar(size='24')
- status-indicator(v-if='rdr.isEnabled', positive, pulse)
- status-indicator(v-else, negative, pulse)
- v-divider.my-0(v-if='n < core.children.length - 1')
-
- v-flex(lg9, xs12)
- v-card.wiki-form.animated.fadeInUp
- v-toolbar(
- color='indigo'
- dark
- flat
- dense
- )
- v-icon.mr-2 {{currentRenderer.icon}}
- .subtitle-1 {{currentRenderer.title}}
- v-spacer
- v-switch(
- dark
- color='white'
- label='Enabled'
- v-model='currentRenderer.isEnabled'
- hide-details
- inset
- )
- v-card-info(color='blue')
- div
- div {{currentRenderer.description}}
- span.caption: a(href='https://docs.requarks.io/en/rendering', target='_blank') Documentation
- v-card-text.pb-4.pl-4
- .overline.mb-5 Rendering Module Configuration
- .body-2.ml-3(v-if='!currentRenderer.config || currentRenderer.config.length < 1'): em This rendering module has no configuration options you can modify.
- template(v-else, v-for='(cfg, idx) in currentRenderer.config')
- v-select(
- v-if='cfg.value.type === "string" && cfg.value.enum'
- outlined
- :items='cfg.value.enum'
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- :class='cfg.value.hint ? "mb-2" : ""'
- color='indigo'
- )
- v-switch(
- v-else-if='cfg.value.type === "boolean"'
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- color='indigo'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- inset
- )
- v-text-field(
- v-else
- outlined
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- :class='cfg.value.hint ? "mb-2" : ""'
- color='indigo'
- )
- v-divider.my-5(v-if='idx < currentRenderer.config.length - 1')
- v-card-chin
- v-spacer
- .caption.pr-3.grey--text Module: {{ currentRenderer.key }}
-
-
-
-
-
diff --git a/client/components/admin/admin-search.vue b/client/components/admin/admin-search.vue
deleted file mode 100644
index cf58b5db..00000000
--- a/client/components/admin/admin-search.vue
+++ /dev/null
@@ -1,217 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row, wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-search.svg', alt='Search Engine', style='width: 80px;')
- .admin-header-title
- .headline.primary--text.animated.fadeInLeft {{$t('admin:search.title')}}
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s {{$t('admin:search.subtitle')}}
- v-spacer
- v-btn.mr-3.animated.fadeInDown.wait-p3s(icon, outlined, color='grey', href='https://docs.requarks.io/search', target='_blank')
- v-icon mdi-help-circle
- v-btn.animated.fadeInDown.wait-p2s(icon, outlined, color='grey', @click='refresh')
- v-icon mdi-refresh
- v-btn.mx-3.animated.fadeInDown.wait-p1s(color='black', dark, depressed, @click='rebuild')
- v-icon(left) mdi-cached
- span {{$t('admin:search.rebuildIndex')}}
- v-btn.animated.fadeInDown(color='success', @click='save', depressed, large)
- v-icon(left) mdi-check
- span {{$t('common:actions.apply')}}
-
- v-flex(lg3, xs12)
- v-card.animated.fadeInUp
- v-toolbar(flat, color='primary', dark, dense)
- .subtitle-1 {{$t('admin:search.searchEngine')}}
- v-list.py-0(two-line, dense)
- template(v-for='(eng, idx) in engines')
- v-list-item(:key='eng.key', @click='selectedEngine = eng.key', :disabled='!eng.isAvailable')
- v-list-item-avatar(size='24')
- v-icon(color='grey', v-if='!eng.isAvailable') mdi-minus-box-outline
- v-icon(color='primary', v-else-if='eng.key === selectedEngine') mdi-checkbox-marked-circle-outline
- v-icon(color='grey', v-else) mdi-checkbox-blank-circle-outline
- v-list-item-content
- v-list-item-title.body-2(:class='!eng.isAvailable ? `grey--text` : (selectedEngine === eng.key ? `primary--text` : ``)') {{ eng.title }}
- v-list-item-subtitle: .caption(:class='!eng.isAvailable ? `grey--text text--lighten-1` : (selectedEngine === eng.key ? `blue--text ` : ``)') {{ eng.description }}
- v-list-item-avatar(v-if='selectedEngine === eng.key', size='24')
- v-icon.animated.fadeInLeft(color='primary', large) mdi-chevron-right
- v-divider(v-if='idx < engines.length - 1')
-
- v-flex(lg9, xs12)
- v-card.animated.fadeInUp.wait-p2s
- v-toolbar(color='primary', dense, flat, dark)
- .subtitle-1 {{engine.title}}
- v-card-info(color='blue')
- div
- div {{engine.description}}
- span.caption: a(:href='engine.website') {{engine.website}}
- v-spacer
- .admin-providerlogo
- img(:src='engine.logo', :alt='engine.title')
- v-card-text
- .overline.mb-5 {{$t('admin:search.engineConfig')}}
- .body-2.ml-3(v-if='!engine.config || engine.config.length < 1'): em {{$t('admin:search.engineNoConfig')}}
- template(v-else, v-for='cfg in engine.config')
- v-select(
- v-if='cfg.value.type === "string" && cfg.value.enum'
- outlined
- :items='cfg.value.enum'
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- prepend-icon='mdi-cog-box'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- :class='cfg.value.hint ? "mb-2" : ""'
- )
- v-switch.mb-3(
- v-else-if='cfg.value.type === "boolean"'
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- color='primary'
- prepend-icon='mdi-cog-box'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- inset
- )
- v-textarea(
- v-else-if='cfg.value.type === "string" && cfg.value.multiline'
- outlined
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- prepend-icon='mdi-cog-box'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- :class='cfg.value.hint ? "mb-2" : ""'
- )
- v-text-field(
- v-else
- outlined
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- prepend-icon='mdi-cog-box'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- :class='cfg.value.hint ? "mb-2" : ""'
- )
-
-
-
-
-
diff --git a/client/components/admin/admin-security.vue b/client/components/admin/admin-security.vue
deleted file mode 100644
index 7a8d305b..00000000
--- a/client/components/admin/admin-security.vue
+++ /dev/null
@@ -1,444 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-private.svg', alt='Security', style='width: 80px;')
- .admin-header-title
- .headline.primary--text.animated.fadeInLeft {{ $t('admin:security.title') }}
- .subtitle-1.grey--text.animated.fadeInLeft {{ $t('admin:security.subtitle') }}
- v-spacer
- v-btn.animated.fadeInDown(color='success', depressed, @click='save', large)
- v-icon(left) mdi-check
- span {{$t('common:actions.apply')}}
- v-form.pt-3
- v-layout(row wrap)
- v-flex(lg6 xs12)
- v-card.animated.fadeInUp
- v-toolbar(color='red darken-2', dark, dense, flat)
- v-toolbar-title.subtitle-1 Security
- v-card-info(color='red')
- span Make sure to understand the implications before turning on / off a security feature.
- v-card-text
- v-switch(
- inset
- label='Block Open Redirect'
- color='red darken-2'
- v-model='config.securityOpenRedirect'
- persistent-hint
- hint='Prevents user controlled URLs from directing to websites outside of your wiki. This provides Open Redirect protection.'
- )
-
- v-divider.mt-3
- v-switch.mt-3(
- inset
- label='Block IFrame Embedding'
- color='red darken-2'
- v-model='config.securityIframe'
- persistent-hint
- hint='Prevents other websites from embedding your wiki in an iframe. This provides clickjacking protection.'
- )
-
- v-divider.mt-3
- v-switch(
- inset
- label='Same Origin Referrer Policy'
- color='red darken-2'
- v-model='config.securityReferrerPolicy'
- persistent-hint
- hint='Limits the referrer header to same origin.'
- )
-
- v-divider.mt-3
- v-switch(
- inset
- label='Trust X-Forwarded-* Proxy Headers'
- color='red darken-2'
- v-model='config.securityTrustProxy'
- persistent-hint
- hint='Should be enabled when using a reverse-proxy like nginx, apache, CloudFlare, etc in front of Wiki.js. Turn off otherwise.'
- )
-
- //- v-divider.mt-3
- //- v-switch(
- //- inset
- //- label='Subresource Integrity (SRI)'
- //- color='red darken-2'
- //- v-model='config.securitySRI'
- //- persistent-hint
- //- hint='This ensure that resources such as CSS and JS files are not altered during delivery.'
- //- disabled
- //- )
-
- v-divider.mt-3
- v-switch(
- inset
- label='Enforce HSTS'
- color='red darken-2'
- v-model='config.securityHSTS'
- persistent-hint
- hint='This ensures the connection cannot be established through an insecure HTTP connection.'
- )
- v-select.mt-5(
- outlined
- label='HSTS Max Age'
- :items='hstsDurations'
- v-model='config.securityHSTSDuration'
- prepend-icon='mdi-subdirectory-arrow-right'
- :disabled='!config.securityHSTS'
- hide-details
- style='max-width: 450px;'
- )
- .pl-11.mt-3
- .caption Defines the duration for which the server should only deliver content through HTTPS.
- .caption It's a good idea to start with small values and make sure that nothing breaks on your wiki before moving to longer values.
-
- //- v-divider.mt-3
- //- v-switch(
- //- inset
- //- label='Enforce CSP'
- //- color='red darken-2'
- //- v-model='config.securityCSP'
- //- persistent-hint
- //- hint='Restricts scripts to pre-approved content sources.'
- //- disabled
- //- )
- //- v-textarea.mt-5(
- //- label='CSP Directives'
- //- outlined
- //- v-model='config.securityCSPDirectives'
- //- prepend-icon='mdi-subdirectory-arrow-right'
- //- persistent-hint
- //- hint='One directive per line.'
- //- disabled
- //- )
-
- v-flex(lg6 xs12)
- v-card.animated.fadeInUp.wait-p2s
- v-toolbar(color='primary', dark, dense, flat)
- v-toolbar-title.subtitle-1 {{ $t('admin:security.uploads') }}
- v-card-info(color='blue')
- span {{$t('admin:security.uploadsInfo')}}
- v-card-text
- v-text-field.mt-3(
- outlined
- :label='$t(`admin:security.maxUploadSize`)'
- required
- v-model='config.uploadMaxFileSize'
- prepend-icon='mdi-progress-upload'
- :hint='$t(`admin:security.maxUploadSizeHint`)'
- persistent-hint
- :suffix='$t(`admin:security.maxUploadSizeSuffix`)'
- style='max-width: 450px;'
- )
- v-text-field.mt-3(
- outlined
- :label='$t(`admin:security.maxUploadBatch`)'
- required
- v-model='config.uploadMaxFiles'
- prepend-icon='mdi-upload-lock'
- :hint='$t(`admin:security.maxUploadBatchHint`)'
- persistent-hint
- :suffix='$t(`admin:security.maxUploadBatchSuffix`)'
- style='max-width: 450px;'
- )
- v-divider.mt-3
- v-switch(
- inset
- label='Scan and Sanitize SVG Uploads'
- color='primary'
- v-model='config.uploadScanSVG'
- persistent-hint
- hint='Should SVG uploads be scanned for vulnerabilities and stripped of any potentially unsafe content.'
- )
- v-divider.mt-3
- v-switch(
- inset
- label='Force Download of Unsafe Extensions'
- color='primary'
- v-model='config.uploadForceDownload'
- persistent-hint
- hint='Should non-image files be forced as downloads when accessed directly. This prevents potential XSS attacks via unsafe file extensions uploads.'
- )
-
- v-card.mt-3.animated.fadeInUp.wait-p2s
- v-toolbar(flat, color='primary', dark, dense)
- .subtitle-1 {{$t('admin:security.login')}}
- //- v-card-info(color='blue')
- //- span {{$t('admin:security.loginInfo')}}
- .overline.grey--text.pa-4 {{$t('admin:security.loginScreen')}}
- .px-4.pb-3
- v-text-field(
- outlined
- :label='$t(`admin:security.loginBgUrl`)'
- v-model='config.authLoginBgUrl'
- :hint='$t(`admin:security.loginBgUrlHint`)'
- persistent-hint
- prepend-icon='mdi-image-area'
- append-icon='mdi-folder-image'
- @click:append='browseLoginBg'
- )
- v-switch(
- inset
- :label='$t(`admin:security.bypassLogin`)'
- color='primary'
- v-model='config.authAutoLogin'
- prepend-icon='mdi-fast-forward'
- persistent-hint
- :hint='$t(`admin:security.bypassLoginHint`)'
- )
- v-switch(
- inset
- :label='$t(`admin:security.hideLocalLogin`)'
- color='primary'
- v-model='config.authHideLocal'
- prepend-icon='mdi-eye-off-outline'
- persistent-hint
- :hint='$t(`admin:security.hideLocalLoginHint`)'
- )
- v-divider.mt-3
- .overline.grey--text.pa-4 {{$t('admin:security.loginSecurity')}}
- .px-4.pb-3
- v-switch.mt-0(
- inset
- :label='$t(`admin:security.enforce2fa`)'
- color='primary'
- v-model='config.authEnforce2FA'
- prepend-icon='mdi-two-factor-authentication'
- :hint='$t(`admin:security.enforce2faHint`)'
- persistent-hint
- )
- v-divider.mt-3
- .overline.grey--text.pa-4 {{$t('admin:security.jwt')}}
- .px-4.pb-3
- v-text-field(
- v-model='config.authJwtAudience'
- outlined
- prepend-icon='mdi-account-group-outline'
- :label='$t(`admin:auth.jwtAudience`)'
- :hint='$t(`admin:auth.jwtAudienceHint`)'
- persistent-hint
- )
- v-text-field.mt-3(
- v-model='config.authJwtExpiration'
- outlined
- prepend-icon='mdi-clock-outline'
- :label='$t(`admin:auth.tokenExpiration`)'
- :hint='$t(`admin:auth.tokenExpirationHint`)'
- persistent-hint
- )
- v-text-field.mt-3(
- v-model='config.authJwtRenewablePeriod'
- outlined
- prepend-icon='mdi-update'
- :label='$t(`admin:auth.tokenRenewalPeriod`)'
- :hint='$t(`admin:auth.tokenRenewalPeriodHint`)'
- persistent-hint
- )
-
- component(:is='activeModal')
-
-
-
-
-
diff --git a/client/components/admin/admin-ssl.vue b/client/components/admin/admin-ssl.vue
deleted file mode 100644
index 71991b5c..00000000
--- a/client/components/admin/admin-ssl.vue
+++ /dev/null
@@ -1,269 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-validation.svg', alt='SSL', style='width: 80px;')
- .admin-header-title
- .headline.primary--text.animated.fadeInLeft {{ $t('admin:ssl.title') }}
- .subtitle-1.grey--text.animated.fadeInLeft {{ $t('admin:ssl.subtitle') }}
- v-spacer
- v-btn.animated.fadeInDown(
- v-if='info.sslProvider === `letsencrypt` && info.httpsPort > 0'
- color='black'
- dark
- depressed
- @click='renewCertificate'
- large
- :loading='loadingRenew'
- )
- v-icon(left) mdi-cached
- span {{$t('admin:ssl.renewCertificate')}}
- v-form.pt-3
- v-layout(row wrap)
- v-flex(lg6 xs12)
- v-card.animated.fadeInUp
- v-subheader {{ $t('admin:ssl.currentState') }}
- v-list(two-line, dense)
- v-list-item
- v-list-item-avatar
- v-icon.indigo.white--text mdi-handshake
- v-list-item-content
- v-list-item-title {{ $t(`admin:ssl.provider`) }}
- v-list-item-subtitle {{ providerTitle }}
- template(v-if='info.sslProvider === `letsencrypt` && info.httpsPort > 0')
- v-list-item
- v-list-item-avatar
- v-icon.indigo.white--text mdi-application
- v-list-item-content
- v-list-item-title {{ $t(`admin:ssl.domain`) }}
- v-list-item-subtitle {{ info.sslDomain }}
- v-list-item
- v-list-item-avatar
- v-icon.indigo.white--text mdi-at
- v-list-item-content
- v-list-item-title {{ $t('admin:ssl.subscriberEmail') }}
- v-list-item-subtitle {{ info.sslSubscriberEmail }}
- v-list-item
- v-list-item-avatar
- v-icon.indigo.white--text mdi-calendar-remove-outline
- v-list-item-content
- v-list-item-title {{ $t('admin:ssl.expiration') }}
- v-list-item-subtitle {{ info.sslExpirationDate | moment('calendar') }}
- v-list-item
- v-list-item-avatar
- v-icon.indigo.white--text mdi-traffic-light
- v-list-item-content
- v-list-item-title {{ $t(`admin:ssl.status`) }}
- v-list-item-subtitle {{ info.sslStatus }}
-
- v-flex(lg6 xs12)
- v-card.animated.fadeInUp.wait-p2s
- v-subheader {{ $t('admin:ssl.ports') }}
- v-list(two-line, dense)
- v-list-item
- v-list-item-avatar
- v-icon.blue.white--text mdi-lock-open-variant
- v-list-item-content
- v-list-item-title {{ $t(`admin:ssl.httpPort`) }}
- v-list-item-subtitle {{ info.httpPort }}
- template(v-if='info.httpsPort > 0')
- v-divider
- v-list-item
- v-list-item-avatar
- v-icon.green.white--text mdi-lock
- v-list-item-content
- v-list-item-title {{ $t(`admin:ssl.httpsPort`) }}
- v-list-item-subtitle {{ info.httpsPort }}
- v-divider
- v-list-item
- v-list-item-avatar
- v-icon.indigo.white--text mdi-sign-direction
- v-list-item-content
- v-list-item-title {{ $t(`admin:ssl.httpPortRedirect`) }}
- v-list-item-subtitle {{ info.httpRedirection }}
- v-list-item-action
- v-btn.red--text(
- v-if='info.httpRedirection'
- depressed
- :color='$vuetify.theme.dark ? `red darken-4` : `red lighten-5`'
- :class='$vuetify.theme.dark ? `text--lighten-5` : `text--darken-2`'
- @click='toggleRedir'
- :loading='loadingRedir'
- )
- v-icon(left) mdi-power
- span {{$t('admin:ssl.httpPortRedirectTurnOff')}}
- v-btn.green--text(
- v-else
- depressed
- :color='$vuetify.theme.dark ? `green darken-4` : `green lighten-5`'
- :class='$vuetify.theme.dark ? `text--lighten-5` : `text--darken-2`'
- @click='toggleRedir'
- :loading='loadingRedir'
- )
- v-icon(left) mdi-power
- span {{$t('admin:ssl.httpPortRedirectTurnOn')}}
-
- v-dialog(
- v-model='loadingRenew'
- persistent
- max-width='450'
- )
- v-card(color='black', dark)
- v-card-text.pa-10.text-center
- semipolar-spinner.animated.fadeIn(
- :animation-duration='1500'
- :size='65'
- color='#FFF'
- style='margin: 0 auto;'
- )
- .mt-5.body-1.white--text {{$t('admin:ssl.renewCertificateLoadingTitle')}}
- .caption.mt-4 {{$t('admin:ssl.renewCertificateLoadingSubtitle')}}
-
-
-
-
-
-
diff --git a/client/components/admin/admin-stats.vue b/client/components/admin/admin-stats.vue
deleted file mode 100644
index cbfd09a3..00000000
--- a/client/components/admin/admin-stats.vue
+++ /dev/null
@@ -1,32 +0,0 @@
-
- v-container(fluid, fill-height)
- v-layout(row wrap)
- v-flex(xs12)
- .admin-header-icon: v-icon(size='80', color='grey lighten-2') show_chart
- .headline.primary--text Statistics
- .subtitle-1.grey--text Useful information about your wiki
- .pa-3
- fingerprint-spinner(
- :animation-duration='1500'
- :size='128'
- color='#e91e63'
- )
- .caption.pink--text.mt-3 Compiling latest data...
-
-
-
-
-
diff --git a/client/components/admin/admin-storage.vue b/client/components/admin/admin-storage.vue
deleted file mode 100644
index fae1d2bd..00000000
--- a/client/components/admin/admin-storage.vue
+++ /dev/null
@@ -1,372 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row, wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-cloud-storage.svg', alt='Storage', style='width: 80px;')
- .admin-header-title
- .headline.primary--text.animated.fadeInLeft {{$t('admin:storage.title')}}
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p4s {{$t('admin:storage.subtitle')}}
- v-spacer
- v-btn.animated.fadeInDown.wait-p3s(icon, outlined, color='grey', href='https://docs.requarks.io/storage', target='_blank')
- v-icon mdi-help-circle
- v-btn.mx-3.animated.fadeInDown.wait-p2s(icon, outlined, color='grey', @click='refresh')
- v-icon mdi-refresh
- v-btn.animated.fadeInDown(color='success', @click='save', depressed, large)
- v-icon(left) mdi-check
- span {{$t('common:actions.apply')}}
-
- v-flex(lg3, xs12)
- v-card.animated.fadeInUp
- v-toolbar(flat, color='primary', dark, dense)
- .subtitle-1 {{$t('admin:storage.targets')}}
- v-list(two-line, dense).py-0
- template(v-for='(tgt, idx) in targets')
- v-list-item(:key='tgt.key', @click='selectedTarget = tgt.key', :disabled='!tgt.isAvailable')
- v-list-item-avatar(size='24')
- v-icon(color='grey', v-if='!tgt.isAvailable') mdi-minus-box-outline
- v-icon(color='primary', v-else-if='tgt.isEnabled', v-ripple, @click='tgt.key !== `local` && (tgt.isEnabled = false)') mdi-checkbox-marked-outline
- v-icon(color='grey', v-else, v-ripple, @click='tgt.isEnabled = true') mdi-checkbox-blank-outline
- v-list-item-content
- v-list-item-title.body-2(:class='!tgt.isAvailable ? `grey--text` : (selectedTarget === tgt.key ? `primary--text` : ``)') {{ tgt.title }}
- v-list-item-subtitle: .caption(:class='!tgt.isAvailable ? `grey--text text--lighten-1` : (selectedTarget === tgt.key ? `blue--text ` : ``)') {{ tgt.description }}
- v-list-item-avatar(v-if='selectedTarget === tgt.key', size='24')
- v-icon.animated.fadeInLeft(color='primary', large) mdi-chevron-right
- v-divider(v-if='idx < targets.length - 1')
-
- v-card.mt-3.animated.fadeInUp.wait-p2s
- v-toolbar(flat, :color='$vuetify.theme.dark ? `grey darken-3-l5` : `grey darken-3`', dark, dense)
- .subtitle-1 {{$t('admin:storage.status')}}
- v-spacer
- looping-rhombuses-spinner(
- :animation-duration='5000'
- :rhombus-size='10'
- color='#FFF'
- )
- v-list.py-0(two-line, dense)
- template(v-for='(tgt, n) in status')
- v-list-item(:key='tgt.key')
- template(v-if='tgt.status === `pending`')
- v-list-item-avatar(color='purple')
- v-icon(color='white') mdi-clock-outline
- v-list-item-content
- v-list-item-title.body-2 {{tgt.title}}
- v-list-item-subtitle.purple--text.caption {{tgt.status}}
- v-list-item-action
- v-progress-circular(indeterminate, :size='20', :width='2', color='purple')
- template(v-else-if='tgt.status === `operational`')
- v-list-item-avatar(color='green')
- v-icon(color='white') mdi-check-circle
- v-list-item-content
- v-list-item-title.body-2 {{tgt.title}}
- v-list-item-subtitle.green--text.caption {{$t('admin:storage.lastSync', { time: $options.filters.moment(tgt.lastAttempt, 'from') })}}
- template(v-else)
- v-list-item-avatar(color='red')
- v-icon(color='white') mdi-close-circle-outline
- v-list-item-content
- v-list-item-title.body-2 {{tgt.title}}
- v-list-item-subtitle.red--text.caption {{$t('admin:storage.lastSyncAttempt', { time: $options.filters.moment(tgt.lastAttempt, 'from') })}}
- v-list-item-action
- v-menu
- template(v-slot:activator='{ on }')
- v-btn(icon, v-on='on')
- v-icon(color='red') mdi-information
- v-card(width='450')
- v-toolbar(flat, color='red', dark, dense) {{$t('admin:storage.errorMsg')}}
- v-card-text {{tgt.message}}
-
- v-divider(v-if='n < status.length - 1')
- v-list-item(v-if='status.length < 1')
- em {{$t('admin:storage.noTarget')}}
-
- v-flex(xs12, lg9)
- v-card.wiki-form.animated.fadeInUp.wait-p2s
- v-toolbar(color='primary', dense, flat, dark)
- .subtitle-1 {{target.title}}
- v-spacer
- v-switch(
- dark
- color='blue lighten-5'
- label='Active'
- v-model='target.isEnabled'
- hide-details
- inset
- )
- v-card-info(color='blue')
- div
- div {{target.description}}
- span.caption: a(:href='target.website') {{target.website}}
- v-spacer
- .admin-providerlogo
- img(:src='target.logo', :alt='target.title')
- v-card-text
- v-form
- i18next.body-2(path='admin:storage.targetState', tag='div', v-if='target.isEnabled')
- v-chip(color='green', small, dark, label, place='state') {{$t('admin:storage.targetStateActive')}}
- i18next.body-2(path='admin:storage.targetState', tag='div', v-else)
- v-chip(color='red', small, dark, label, place='state') {{$t('admin:storage.targetStateInactive')}}
- v-divider.mt-3
- .overline.my-5 {{$t('admin:storage.targetConfig')}}
- .body-2.ml-3(v-if='!target.config || target.config.length < 1'): em {{$t('admin:storage.noConfigOption')}}
- template(v-else, v-for='cfg in target.config')
- v-select(
- v-if='cfg.value.type === "string" && cfg.value.enum'
- outlined
- :items='cfg.value.enum'
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- prepend-icon='mdi-cog-box'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- :class='cfg.value.hint ? "mb-2" : ""'
- )
- v-switch.mb-3(
- v-else-if='cfg.value.type === "boolean"'
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- color='primary'
- prepend-icon='mdi-cog-box'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- inset
- )
- v-textarea(
- v-else-if='cfg.value.type === "string" && cfg.value.multiline'
- outlined
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- prepend-icon='mdi-cog-box'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- :class='cfg.value.hint ? "mb-2" : ""'
- )
- v-text-field(
- v-else
- outlined
- :key='cfg.key'
- :label='cfg.value.title'
- v-model='cfg.value.value'
- prepend-icon='mdi-cog-box'
- :hint='cfg.value.hint ? cfg.value.hint : ""'
- persistent-hint
- :class='cfg.value.hint ? "mb-2" : ""'
- )
- v-divider.mt-3
- .overline.my-5 {{$t('admin:storage.syncDirection')}}
- .body-2.ml-3 {{$t('admin:storage.syncDirectionSubtitle')}}
- .pr-3.pt-3
- v-radio-group.ml-3.py-0(v-model='target.mode')
- v-radio(
- :label='$t(`admin:storage.syncDirBi`)'
- color='primary'
- value='sync'
- :disabled='target.supportedModes.indexOf(`sync`) < 0'
- )
- v-radio(
- :label='$t(`admin:storage.syncDirPush`)'
- color='primary'
- value='push'
- :disabled='target.supportedModes.indexOf(`push`) < 0'
- )
- v-radio(
- :label='$t(`admin:storage.syncDirPull`)'
- color='primary'
- value='pull'
- :disabled='target.supportedModes.indexOf(`pull`) < 0'
- )
- .body-2.ml-3
- strong {{$t('admin:storage.syncDirBi')}} #[em.red--text.text--lighten-2(v-if='target.supportedModes.indexOf(`sync`) < 0') {{$t('admin:storage.unsupported')}}]
- .pb-3 {{$t('admin:storage.syncDirBiHint')}}
- strong {{$t('admin:storage.syncDirPush')}} #[em.red--text.text--lighten-2(v-if='target.supportedModes.indexOf(`push`) < 0') {{$t('admin:storage.unsupported')}}]
- .pb-3 {{$t('admin:storage.syncDirPushHint')}}
- strong {{$t('admin:storage.syncDirPull')}} #[em.red--text.text--lighten-2(v-if='target.supportedModes.indexOf(`pull`) < 0') {{$t('admin:storage.unsupported')}}]
- .pb-3 {{$t('admin:storage.syncDirPullHint')}}
-
- template(v-if='target.hasSchedule')
- v-divider.mt-3
- .overline.my-5 {{$t('admin:storage.syncSchedule')}}
- .body-2.ml-3 {{$t('admin:storage.syncScheduleHint')}}
- .pa-3
- duration-picker(v-model='target.syncInterval')
- i18next.caption.mt-3(path='admin:storage.syncScheduleCurrent', tag='div')
- strong(place='schedule') {{getDefaultSchedule(target.syncInterval)}}
- i18next.caption(path='admin:storage.syncScheduleDefault', tag='div')
- strong(place='schedule') {{getDefaultSchedule(target.syncIntervalDefault)}}
-
- template(v-if='target.actions && target.actions.length > 0')
- v-divider.mt-3
- .overline.my-5 {{$t('admin:storage.actions')}}
- v-alert(outlined, :value='!target.isEnabled', color='red', icon='mdi-alert')
- .body-2 {{$t('admin:storage.actionsInactiveWarn')}}
- v-container.pt-0(grid-list-xl, fluid)
- v-layout(row, wrap, fill-height)
- v-flex(xs12, lg6, xl4, v-for='act of target.actions', :key='act.handler')
- v-card.radius-7.grey(flat, :class='$vuetify.theme.dark ? `darken-3-d5` : `lighten-3`', height='100%')
- v-card-text
- .subtitle-1(v-html='act.label')
- .body-2.mt-4(v-html='act.hint')
- v-btn.mx-0.mt-5(
- @click='executeAction(target.key, act.handler)'
- outlined
- :color='$vuetify.theme.dark ? `blue` : `primary`'
- :disabled='runningAction || !target.isEnabled'
- :loading='runningActionHandler === act.handler'
- ) {{$t('admin:storage.actionRun')}}
-
-
-
-
-
-
diff --git a/client/components/admin/admin-system.vue b/client/components/admin/admin-system.vue
deleted file mode 100644
index 4f0c8256..00000000
--- a/client/components/admin/admin-system.vue
+++ /dev/null
@@ -1,238 +0,0 @@
-
- v-container.admin-system(fluid, grid-list-lg)
- v-layout(row, wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-tune.svg', alt='System Info', style='width: 80px;')
- .admin-header-title
- .headline.primary--text.animated.fadeInLeft {{ $t('admin:system.title') }}
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s {{ $t('admin:system.subtitle') }}
- v-layout.mt-3(row wrap)
- v-flex(lg6 xs12)
- v-card.animated.fadeInUp
- v-btn.animated.fadeInLeft.wait-p2s.btn-animate-rotate(fab, absolute, :right='!$vuetify.rtl', :left='$vuetify.rtl', top, small, light, @click='refresh'): v-icon(color='grey') mdi-refresh
- v-subheader Wiki.js
- v-list(two-line, dense)
- v-list-item
- v-list-item-avatar
- v-icon.blue.white--text mdi-application-export
- v-list-item-content
- v-list-item-title {{ $t('admin:system.currentVersion') }}
- v-list-item-subtitle {{ info.currentVersion }}
- v-list-item
- v-list-item-avatar
- v-icon.blue.white--text mdi-inbox-arrow-up
- v-list-item-content
- v-list-item-title {{ $t('admin:system.latestVersion') }}
- v-list-item-subtitle {{ info.latestVersion }}
- v-list-item-action
- v-list-item-action-text {{ $t('admin:system.published') }} {{ info.latestVersionReleaseDate | moment('from') }}
- v-card-actions(v-if='info.upgradeCapable && !isLatestVersion && info.platform === `docker`', :class='$vuetify.theme.dark ? `grey darken-3-d5` : `indigo lighten-5`')
- .caption.indigo--text.pl-3(:class='$vuetify.theme.dark ? `text--lighten-4` : ``') Wiki.js can perform the upgrade to the latest version for you.
- v-spacer
- v-btn.px-3(
- color='indigo'
- dark
- @click='performUpgrade'
- )
- v-icon(left) mdi-upload
- span Perform Upgrade
-
- v-card.mt-4.animated.fadeInUp.wait-p2s
- v-subheader {{ $t('admin:system.hostInfo') }}
- v-list(two-line, dense)
- v-list-item
- v-list-item-avatar
- v-avatar.blue-grey(size='40')
- v-icon(color='white') {{platformLogo}}
- v-list-item-content
- v-list-item-title {{ $t('admin:system.os') }}
- v-list-item-subtitle {{ (info.platform === 'docker') ? 'Docker Container (Linux)' : info.operatingSystem }}
- v-list-item
- v-list-item-avatar
- v-icon.blue-grey.white--text mdi-desktop-classic
- v-list-item-content
- v-list-item-title {{ $t('admin:system.hostname') }}
- v-list-item-subtitle {{ info.hostname }}
- v-list-item
- v-list-item-avatar
- v-icon.blue-grey.white--text mdi-cpu-64-bit
- v-list-item-content
- v-list-item-title {{ $t('admin:system.cpuCores') }}
- v-list-item-subtitle {{ info.cpuCores }}
- v-list-item
- v-list-item-avatar
- v-icon.blue-grey.white--text mdi-memory
- v-list-item-content
- v-list-item-title {{ $t('admin:system.totalRAM') }}
- v-list-item-subtitle {{ info.ramTotal }}
- v-list-item
- v-list-item-avatar
- v-icon.blue-grey.white--text mdi-iframe-outline
- v-list-item-content
- v-list-item-title {{ $t('admin:system.workingDirectory') }}
- v-list-item-subtitle {{ info.workingDirectory }}
- v-list-item
- v-list-item-avatar
- v-icon.blue-grey.white--text mdi-card-bulleted-settings-outline
- v-list-item-content
- v-list-item-title {{ $t('admin:system.configFile') }}
- v-list-item-subtitle {{ info.configFile }}
-
- v-flex(lg6 xs12)
- v-card.pb-3.animated.fadeInUp.wait-p4s
- v-subheader Node.js
- v-list(dense)
- v-list-item
- v-list-item-avatar
- v-avatar.light-green(size='40')
- v-icon(color='white') mdi-nodejs
- v-list-item-content
- v-list-item-title {{ info.nodeVersion }}
-
- v-divider.mt-3
- v-subheader {{ info.dbType }}
- v-list(dense)
- v-list-item
- v-list-item-avatar
- v-avatar.indigo.darken-1(size='40')
- v-icon(color='white') mdi-database
- v-list-item-content
- v-list-item-title(v-html='dbVersion')
- v-list-item-subtitle {{ info.dbHost }}
-
- v-alert.mt-3.mx-4(:value='isDbLimited', color='deep-orange darken-2', icon='mdi-alert', dark) {{ $t('admin:system.dbPartialSupport') }}
-
- v-dialog(
- v-model='isUpgrading'
- persistent
- width='450'
- )
- v-card.blue.darken-5(dark)
- v-card-text.text-center.pa-10
- self-building-square-spinner(
- :animation-duration='4000'
- :size='40'
- color='#FFF'
- style='margin: 0 auto;'
- )
- .body-2.mt-5.blue--text.text--lighten-4 Your Wiki.js container is being upgraded...
- .caption.blue--text.text--lighten-2 Please wait
- v-progress-linear.mt-5(
- color='blue lighten-2'
- :value='upgradeProgress'
- :buffer-value='upgradeProgress'
- rounded
- :stream='isUpgradingStarted'
- query
- :indeterminate='!isUpgradingStarted'
- )
-
-
-
-
-
diff --git a/client/components/admin/admin-tags.vue b/client/components/admin/admin-tags.vue
deleted file mode 100644
index 0bd000e8..00000000
--- a/client/components/admin/admin-tags.vue
+++ /dev/null
@@ -1,248 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-tags.svg', alt='Tags', style='width: 80px;')
- .admin-header-title
- .headline.primary--text.animated.fadeInLeft {{$t('tags.title')}}
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p4s {{$t('tags.subtitle')}}
- v-spacer
- v-btn.animated.fadeInDown(outlined, color='grey', @click='refresh', icon)
- v-icon mdi-refresh
- v-container.pa-0.mt-3(fluid, grid-list-lg)
- v-layout(row)
- v-flex(style='flex: 0 0 350px;')
- v-card.animated.fadeInUp
- v-toolbar(:color='$vuetify.theme.dark ? `grey darken-3-d5` : `grey lighten-4`', flat)
- v-text-field(
- v-model='filter'
- :label='$t(`admin:tags.filter`)'
- hide-details
- single-line
- solo
- flat
- dense
- color='teal'
- :background-color='$vuetify.theme.dark ? `grey darken-4` : `grey lighten-2`'
- prepend-inner-icon='mdi-magnify'
- )
- v-divider
- v-list.py-2(dense, nav)
- v-list-item(v-if='tags.length < 1')
- v-list-item-avatar(size='24'): v-icon(color='grey') mdi-compass-off
- v-list-item-content
- .caption.grey--text {{$t('tags.emptyList')}}
- v-list-item(
- v-for='tag of filteredTags'
- :key='tag.id'
- :class='(tag.id === current.id) ? "teal" : ""'
- @click='selectTag(tag)'
- )
- v-list-item-avatar(size='24', tile): v-icon(size='18', :color='tag.id === current.id ? `white` : `teal`') mdi-tag
- v-list-item-title(:class='tag.id === current.id ? `white--text` : ``') {{tag.tag}}
- v-flex.animated.fadeInUp.wait-p2s
- template(v-if='current.id')
- v-card
- v-toolbar(dense, color='teal', flat, dark)
- .subtitle-1 {{$t('tags.edit')}}
- v-spacer
- v-btn.pl-4(
- color='white'
- dark
- outlined
- small
- :href='`/t/` + current.tag'
- )
- span.text-none {{$t('admin:tags.viewLinkedPages')}}
- v-icon(right) mdi-chevron-right
- v-card-text
- v-text-field(
- outlined
- :label='$t("tags.tag")'
- prepend-icon='mdi-tag'
- v-model='current.tag'
- counter='255'
- )
- v-text-field(
- outlined
- :label='$t("tags.label")'
- prepend-icon='mdi-format-title'
- v-model='current.title'
- hide-details
- )
- v-card-chin
- i18next.caption.pl-3(path='admin:tags.date', tag='div')
- strong(place='created') {{current.createdAt | moment('from')}}
- strong(place='updated') {{current.updatedAt | moment('from')}}
- v-spacer
- v-dialog(v-model='deleteTagDialog', max-width='500')
- template(v-slot:activator='{ on }')
- v-btn(color='red', outlined, v-on='on')
- v-icon(color='red') mdi-trash-can-outline
- v-card
- .dialog-header.is-red {{$t('admin:tags.deleteConfirm')}}
- v-card-text.pa-4
- i18next(tag='span', path='admin:tags.deleteConfirmText')
- strong(place='tag') {{ current.tag }}
- v-card-actions
- v-spacer
- v-btn(text, @click='deleteTagDialog = false') {{$t('common:actions.cancel')}}
- v-btn(color='red', dark, @click='deleteTag(current)') {{$t('common:actions.delete')}}
- v-btn.px-5.mr-2(color='success', depressed, dark, @click='saveTag(current)')
- v-icon(left) mdi-content-save
- span {{$t('common:actions.save')}}
- v-card(v-else)
- v-card-text.grey--text(v-if='tags.length > 0') {{$t('tags.noSelectionText')}}
- v-card-text.grey--text(v-else) {{$t('tags.noItemsText')}}
-
-
-
-
-
diff --git a/client/components/admin/admin-theme.vue b/client/components/admin/admin-theme.vue
deleted file mode 100644
index 83ec5fac..00000000
--- a/client/components/admin/admin-theme.vue
+++ /dev/null
@@ -1,255 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-paint-palette.svg', alt='Theme', style='width: 80px;')
- .admin-header-title
- .headline.primary--text.animated.fadeInLeft {{$t('admin:theme.title')}}
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s {{$t('admin:theme.subtitle')}}
- v-spacer
- v-btn.animated.fadeInRight(color='success', depressed, @click='save', large, :loading='loading')
- v-icon(left) mdi-check
- span {{$t('common:actions.apply')}}
- v-form.pt-3
- v-layout(row wrap)
- v-flex(lg6 xs12)
- v-card.animated.fadeInUp
- v-toolbar(color='primary', dark, dense, flat)
- v-toolbar-title.subtitle-1 {{$t('admin:theme.title')}}
- v-card-text
- v-select(
- :items='themes'
- outlined
- prepend-icon='mdi-palette'
- v-model='config.theme'
- :label='$t(`admin:theme.siteTheme`)'
- persistent-hint
- :hint='$t(`admin:theme.siteThemeHint`)'
- )
- template(slot='item', slot-scope='data')
- v-list-item-avatar
- v-icon.blue--text(dark) mdi-image-filter-frames
- v-list-item-content
- v-list-item-title(v-html='data.item.text')
- v-list-item-sub-title(v-html='data.item.author')
- v-select.mt-3(
- :items='iconsets'
- outlined
- prepend-icon='mdi-paw'
- v-model='config.iconset'
- :label='$t(`admin:theme.iconset`)'
- persistent-hint
- :hint='$t(`admin:theme.iconsetHint`)'
- )
- v-divider.mt-3
- v-switch(
- inset
- v-model='darkMode'
- :label='$t(`admin:theme.darkMode`)'
- color='primary'
- persistent-hint
- :hint='$t(`admin:theme.darkModeHint`)'
- )
-
- v-card.mt-3.animated.fadeInUp.wait-p1s
- v-toolbar(color='primary', dark, dense, flat)
- v-toolbar-title.subtitle-1 {{$t(`admin:theme.options`)}}
- v-spacer
- v-chip(label, color='white', small).primary--text coming soon
- v-card-text
- v-select(
- :items='[]'
- outlined
- prepend-icon='mdi-border-vertical'
- v-model='config.iconset'
- label='Table of Contents Position'
- persistent-hint
- hint='Select whether the table of contents is shown on the left, right or not at all.'
- disabled
- )
-
- v-flex(lg6 xs12)
- //- v-card.animated.fadeInUp.wait-p2s
- //- v-toolbar(color='teal', dark, dense, flat)
- //- v-toolbar-title.subtitle-1 {{$t('admin:theme.downloadThemes')}}
- //- v-spacer
- //- v-chip(label, color='white', small).teal--text coming soon
- //- v-data-table(
- //- :headers='headers',
- //- :items='themes',
- //- hide-default-footer,
- //- item-key='value',
- //- :items-per-page='1000'
- //- )
- //- template(v-slot:item='thm')
- //- td
- //- strong {{thm.item.text}}
- //- td
- //- span {{ thm.item.author }}
- //- td.text-xs-center
- //- v-progress-circular(v-if='thm.item.isDownloading', indeterminate, color='blue', size='20', :width='2')
- //- v-btn(v-else-if='thm.item.isInstalled && thm.item.installDate < thm.item.updatedAt', icon)
- //- v-icon.blue--text mdi-cached
- //- v-btn(v-else-if='thm.item.isInstalled', icon)
- //- v-icon.green--text mdi-check-bold
- //- v-btn(v-else, icon)
- //- v-icon.grey--text mdi-cloud-download
-
- v-card.animated.fadeInUp.wait-p2s
- v-toolbar(color='primary', dark, dense, flat)
- v-toolbar-title.subtitle-1 {{$t(`admin:theme.codeInjection`)}}
- v-card-text
- v-textarea.is-monospaced(
- v-model='config.injectCSS'
- :label='$t(`admin:theme.cssOverride`)'
- outlined
- color='primary'
- persistent-hint
- :hint='$t(`admin:theme.cssOverrideHint`)'
- auto-grow
- )
- i18next.caption.pl-2.ml-1(path='admin:theme.cssOverrideWarning', tag='div')
- strong.red--text(place='caution') {{$t('admin:theme.cssOverrideWarningCaution')}}
- code(place='cssClass') .contents
- v-textarea.is-monospaced.mt-3(
- v-model='config.injectHead'
- :label='$t(`admin:theme.headHtmlInjection`)'
- outlined
- color='primary'
- persistent-hint
- :hint='$t(`admin:theme.headHtmlInjectionHint`)'
- auto-grow
- )
- v-textarea.is-monospaced.mt-2(
- v-model='config.injectBody'
- :label='$t(`admin:theme.bodyHtmlInjection`)'
- outlined
- color='primary'
- persistent-hint
- :hint='$t(`admin:theme.bodyHtmlInjectionHint`)'
- auto-grow
- )
-
-
-
-
-
diff --git a/client/components/admin/admin-users-create.vue b/client/components/admin/admin-users-create.vue
deleted file mode 100644
index 7128149e..00000000
--- a/client/components/admin/admin-users-create.vue
+++ /dev/null
@@ -1,256 +0,0 @@
-
- v-dialog(v-model='isShown', max-width='650', persistent)
- v-card
- .dialog-header.is-short
- v-icon.mr-3(color='white') mdi-plus
- span New User
- v-spacer
- v-btn.mx-0(color='white', outlined, disabled, dark)
- v-icon(left) mdi-database-import
- span Bulk Import
- v-card-text.pt-5
- v-select(
- :items='providers'
- item-text='displayName'
- item-value='key'
- outlined
- prepend-icon='mdi-domain'
- v-model='provider'
- label='Provider'
- )
- v-text-field(
- outlined
- prepend-icon='mdi-at'
- v-model='email'
- label='Email Address'
- key='newUserEmail'
- persistent-hint
- ref='emailInput'
- )
- v-text-field(
- v-if='provider === `local`'
- outlined
- prepend-icon='mdi-lock-outline'
- append-icon='mdi-dice-5'
- v-model='password'
- :label='mustChangePwd ? `Temporary Password` : `Password`'
- counter='255'
- @click:append='generatePwd'
- key='newUserPassword'
- persistent-hint
- )
- v-text-field(
- outlined
- prepend-icon='mdi-account-outline'
- v-model='name'
- label='Name'
- :hint='provider === `local` ? `Can be changed by the user.` : `May be overwritten by the provider during login.`'
- key='newUserName'
- persistent-hint
- )
- v-select.mt-2(
- :items='groups'
- item-text='name'
- item-value='id'
- item-disabled='isSystem'
- outlined
- prepend-icon='mdi-account-group'
- v-model='group'
- label='Assign to Group(s)...'
- hint='Note that you cannot assign users to the Administrators or Guests groups from this dialog.'
- persistent-hint
- clearable
- multiple
- )
- v-divider
- v-checkbox(
- color='primary'
- label='Require password change on first login'
- v-if='provider === `local`'
- v-model='mustChangePwd'
- hide-details
- )
- v-checkbox(
- color='primary'
- label='Send a welcome email'
- hide-details
- v-model='sendWelcomeEmail'
- disabled
- )
- v-card-chin
- v-spacer
- v-btn(text, @click='isShown = false') Cancel
- v-btn.px-3(depressed, color='primary', @click='newUser(false)')
- v-icon(left) mdi-chevron-right
- span Create
- v-btn.px-3(depressed, color='primary', @click='newUser(true)')
- v-icon(left) mdi-chevron-double-right
- span Create and Close
-
-
-
diff --git a/client/components/admin/admin-users-edit.vue b/client/components/admin/admin-users-edit.vue
deleted file mode 100644
index 7b5acae6..00000000
--- a/client/components/admin/admin-users-edit.vue
+++ /dev/null
@@ -1,1077 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row, wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-male-user.svg', :alt='$t(`admin:users.edit`)', style='width: 80px;')
- .admin-header-title
- .headline.blue--text.text--darken-2.animated.fadeInLeft {{$t('admin:users.edit')}}
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s {{user.name}}
- v-spacer
- i18next.pr-4.caption.grey--text.animated.fadeInDown(path='admin:users.id', tag='div')
- strong(place='id') {{user.id}}
- template(v-if='user.isActive')
- status-indicator.mr-3(positive, pulse)
- .caption.green--text {{$t('admin:users.active')}}
- template(v-else)
- status-indicator.mr-3(negative, pulse)
- .caption.red--text {{$t('admin:users.inactive')}}
- template(v-if='user.isVerified')
- status-indicator.mr-3.ml-4(active, pulse)
- .caption.blue--text {{$t('admin:users.verified')}}
- template(v-else)
- status-indicator.mr-3.ml-4(intermediary, pulse)
- .caption.deep-orange--text {{$t('admin:users.unverified')}}
- v-spacer
- v-btn.ml-3.animated.fadeInDown.wait-p3s(color='grey', icon, outlined, to='/users')
- v-icon mdi-arrow-left
- v-menu(offset-y, origin='top right')
- template(v-slot:activator='{ on }')
- v-btn.ml-3.animated.fadeInDown.wait-p2s(color='black', v-on='on', depressed, dark)
- span Actions
- v-icon(right) mdi-chevron-down
- v-list(dense, nav)
- v-list-item(v-if='!user.isActive', @click='activateUser')
- v-list-item-icon
- v-icon(color='purple') mdi-account-key
- v-list-item-title Activate
- v-list-item(v-else, @click='deactivateUser', :disabled='user.id == currentUserId || user.isSystem')
- v-list-item-icon
- v-icon(color='purple') mdi-account-cancel
- v-list-item-title Deactivate
- v-list-item(@click='verifyUser', :disabled='user.isVerified')
- v-list-item-icon
- v-icon(color='blue') mdi-account-check
- v-list-item-title Set as Verified
- v-list-item(@click='deleteUserConfirm', :disabled='user.id == currentUserId || user.isSystem')
- v-list-item-icon
- v-icon(color='red') mdi-trash-can-outline
- v-list-item-title Delete
- v-btn.ml-3.animated.fadeInDown(color='primary', large, depressed, @click='updateUser')
- v-icon(left) mdi-check
- span {{$t('admin:users.updateUser')}}
- v-flex(xs6)
- v-card.animated.fadeInUp
- v-toolbar(color='primary', dense, dark, flat)
- v-icon.mr-2 mdi-information-variant
- span {{$t('admin:users.basicInfo')}}
- v-list.py-0(two-line, dense)
- v-list-item
- v-list-item-avatar(size='32')
- v-icon mdi-email-variant
- v-list-item-content
- v-list-item-title {{$t('admin:users.email')}}
- v-list-item-subtitle {{ user.email }}
- v-list-item-action(v-if='!user.isSystem && user.providerKey === `local`')
- v-menu(
- v-model='editPop.email'
- :close-on-content-click='false'
- min-width='350'
- left
- )
- template(v-slot:activator='{ on }')
- v-btn(icon, color='grey', x-small, v-on='on', @click='focusField(`iptEmail`)')
- v-icon mdi-pencil
- v-card
- v-text-field(
- ref='iptEmail'
- v-model='user.email'
- :label='$t(`admin:users.email`)'
- solo
- hide-details
- append-icon='mdi-check'
- @click:append='editPop.email = false'
- @keydown.enter='editPop.email = false'
- @keydown.esc='editPop.email = false'
- )
-
- v-divider
- v-list-item
- v-list-item-avatar(size='32')
- v-icon mdi-account
- v-list-item-content
- v-list-item-title {{$t('admin:users.displayName')}}
- v-list-item-subtitle {{ user.name }}
- v-list-item-action
- v-menu(
- v-model='editPop.name'
- :close-on-content-click='false'
- min-width='350'
- left
- )
- template(v-slot:activator='{ on }')
- v-btn(icon, color='grey', x-small, v-on='on', @click='focusField(`iptDisplayName`)')
- v-icon mdi-pencil
- v-card
- v-text-field(
- ref='iptDisplayName'
- v-model='user.name'
- :label='$t(`admin:users.displayName`)'
- solo
- hide-details
- append-icon='mdi-check'
- @click:append='editPop.name = false'
- @keydown.enter='editPop.name = false'
- @keydown.esc='editPop.name = false'
- )
-
- v-card.mt-3.animated.fadeInUp.wait-p2s(v-if='!user.isSystem')
- v-toolbar(color='primary', dense, dark, flat)
- v-icon.mr-2 mdi-lock-outline
- span {{$t('admin:users.authentication')}}
- v-list.py-0(two-line, dense)
- v-list-item
- v-list-item-avatar(size='32')
- v-icon mdi-domain
- v-list-item-content
- v-list-item-title {{$t('admin:users.authProvider')}}
- v-list-item-subtitle {{ user.providerName }} #[em.caption ({{ user.providerKey }})]
- template(v-if='user.providerKey === `local`')
- v-divider
- v-list-item
- v-list-item-avatar(size='32')
- v-icon mdi-form-textbox-password
- v-list-item-content
- v-list-item-title {{$t('admin:users.password')}}
- v-list-item-subtitle ••••••••
- v-list-item-action
- v-menu(
- v-model='editPop.newPassword'
- :close-on-content-click='false'
- min-width='350'
- left
- )
- template(v-slot:activator='{ on: menu }')
- v-tooltip(top)
- template(v-slot:activator='{ on: tooltip }')
- v-btn(icon, color='grey', x-small, v-on='{ ...menu, ...tooltip }', @click='focusField(`iptNewPassword`)')
- v-icon mdi-pencil
- span {{$t('admin:users.changePassword')}}
- v-card
- v-text-field(
- ref='iptNewPassword'
- v-model='newPassword'
- :label='$t(`admin:users.newPassword`)'
- solo
- hide-details
- append-icon='mdi-check'
- type='password'
- @click:append='editPop.newPassword = false'
- @keydown.enter='editPop.newPassword = false'
- @keydown.esc='editPop.newPassword = false'
- )
- v-list-item-action
- v-tooltip(top)
- template(v-slot:activator='{ on }')
- v-btn(icon, color='grey', x-small, v-on='on', disabled)
- v-icon mdi-email
- span Send Password Reset Email
- template(v-if='user.providerIs2FACapable')
- v-divider
- v-list-item
- v-list-item-avatar(size='32')
- v-icon mdi-two-factor-authentication
- v-list-item-content
- v-list-item-title {{$t('admin:users.tfa')}}
- v-list-item-subtitle.green--text(v-if='user.tfaIsActive') Active
- v-list-item-subtitle.red--text(v-else) Inactive
- v-list-item-action
- v-tooltip(top)
- template(v-slot:activator='{ on }')
- v-btn(icon, color='grey', x-small, v-on='on', @click='toggle2FA')
- v-icon mdi-power
- span {{$t('admin:users.toggle2FA')}}
- template(v-if='user.providerId')
- v-divider
- v-list-item
- v-list-item-avatar(size='32')
- v-icon mdi-music-accidental-sharp
- v-list-item-content
- v-list-item-title {{$t('admin:users.authProviderId')}}
- v-list-item-subtitle {{ user.providerId }}
- v-card.mt-3.animated.fadeInUp.wait-p4s
- v-toolbar(color='primary', dense, dark, flat)
- v-icon.mr-2 mdi-account-group
- span {{$t('admin:users.groups')}}
- v-list(dense)
- template(v-for='(group, idx) in user.groups')
- v-list-item(:key='`group-` + group.id')
- v-list-item-avatar(size='32')
- v-icon mdi-account-group-outline
- v-list-item-content
- v-list-item-title {{group.name}}
- v-list-item-action(v-if='!user.isSystem')
- v-btn(icon, color='red', x-small, @click='unassignGroup(group.id)')
- v-icon mdi-close
- v-divider(v-if='idx < user.groups.length - 1')
- v-alert.mx-3(v-if='user.groups.length < 1', outlined, color='grey darken-1', icon='mdi-alert')
- .caption {{$t('admin:users.noGroupAssigned')}}
- v-card-chin(v-if='!user.isSystem')
- v-spacer
- v-select(
- ref='iptAssignGroup'
- :items='groups'
- v-model='newGroup'
- :label='$t(`admin:users.selectGroup`)'
- item-value='id'
- item-text='name'
- item-disabled='isSystem'
- solo
- flat
- hide-details
- @keydown.esc='editPop.assignGroup = false'
- style='max-width: 300px;'
- dense
- )
- v-btn.ml-2.px-4(depressed, color='primary', @click='assignGroup', :disabled='newGroup === 0')
- v-icon(left) mdi-clipboard-account-outline
- span {{$t('admin:users.groupAssign')}}
- v-system-bar(window, :color='$vuetify.theme.dark ? `grey darken-4-l3` : `grey lighten-3`')
- v-spacer
- .caption {{$t('admin:users.groupAssignNotice')}}
-
- v-flex(xs6)
- v-card.animated.fadeInUp.wait-p2s
- v-toolbar(color='primary', dense, dark, flat)
- v-icon.mr-2 mdi-account-badge-outline
- span {{$t('admin:users.extendedMetadata')}}
- v-list.py-0(two-line, dense)
- v-list-item
- v-list-item-avatar(size='32')
- v-icon mdi-map-marker
- v-list-item-content
- v-list-item-title {{$t('admin:users.location')}}
- v-list-item-subtitle {{ user.location }}
- v-list-item-action
- v-menu(
- v-model='editPop.location'
- :close-on-content-click='false'
- min-width='350'
- left
- )
- template(v-slot:activator='{ on }')
- v-btn(icon, color='grey', x-small, v-on='on', @click='focusField(`iptLocation`)')
- v-icon mdi-pencil
- v-card
- v-text-field(
- ref='iptLocation'
- v-model='user.location'
- :label='$t(`admin:users.location`)'
- solo
- hide-details
- append-icon='mdi-check'
- @click:append='editPop.location = false'
- @keydown.enter='editPop.location = false'
- @keydown.esc='editPop.location = false'
- )
- v-divider
- v-list-item
- v-list-item-avatar(size='32')
- v-icon mdi-briefcase
- v-list-item-content
- v-list-item-title {{$t('admin:users.jobTitle')}}
- v-list-item-subtitle {{ user.jobTitle }}
- v-list-item-action
- v-menu(
- v-model='editPop.jobTitle'
- :close-on-content-click='false'
- min-width='350'
- left
- )
- template(v-slot:activator='{ on }')
- v-btn(icon, color='grey', x-small, v-on='on', @click='focusField(`iptJobTitle`)')
- v-icon mdi-pencil
- v-card
- v-text-field(
- ref='iptJobTitle'
- v-model='user.jobTitle'
- :label='$t(`admin:users.jobTitle`)'
- solo
- hide-details
- append-icon='mdi-check'
- @click:append='editPop.jobTitle = false'
- @keydown.enter='editPop.jobTitle = false'
- @keydown.esc='editPop.jobTitle = false'
- )
- v-divider
- v-list-item
- v-list-item-avatar(size='32')
- v-icon mdi-map-clock-outline
- v-list-item-content
- v-list-item-title {{$t('admin:users.timezone')}}
- v-list-item-subtitle {{ user.timezone }}
- v-list-item-action
- v-menu(
- v-model='editPop.timezone'
- :close-on-content-click='false'
- min-width='350'
- left
- )
- template(v-slot:activator='{ on }')
- v-btn(icon, color='grey', x-small, v-on='on', @click='focusField(`iptTimezone`)')
- v-icon mdi-pencil
- v-card
- v-select(
- ref='iptTimezone'
- :items='timezones'
- v-model='user.timezone'
- :label='$t(`admin:users.timezone`)'
- solo
- dense
- hide-details
- append-icon='mdi-check'
- @click:append='editPop.timezone = false'
- @keydown.enter='editPop.timezone = false'
- @keydown.esc='editPop.timezone = false'
- )
-
- v-card.mt-3.animated.fadeInUp.wait-p4s
- v-toolbar(color='teal', dark, dense, flat)
- v-toolbar-title
- .subtitle-1 {{$t('profile:activity.title')}}
- v-card-text.grey--text.text--darken-2
- .caption.grey--text {{$t('profile:activity.joinedOn')}}
- .body-2: strong {{ user.createdAt | moment('LLLL') }}
- .caption.grey--text.mt-3 {{$t('profile:activity.lastUpdatedOn')}}
- .body-2: strong {{ user.updatedAt | moment('LLLL') }}
- .caption.grey--text.mt-3 {{$t('profile:activity.lastLoginOn')}}
- .body-2: strong {{ user.lastLoginAt | moment('LLLL') }}
-
- v-card.mt-3.animated.fadeInUp.wait-p6s
- v-toolbar(color='teal', dense, dark, flat)
- v-icon.mr-2 mdi-file-document-box-multiple-outline
- span Content
- v-card-text
- em.caption.grey--text Coming soon
-
- v-dialog(v-model='deleteUserDialog', max-width='500')
- v-card
- .dialog-header.is-red {{$t('admin:users.deleteConfirmTitle')}}
- v-card-text.pt-5
- i18next(path='admin:users.deleteConfirmText', tag='span')
- strong(place='username') {{ user.email }}
- .mt-3 {{$t('admin:users.deleteConfirmReplaceWarn')}}
- v-divider.my-3
- .d-flex.align-center.mt-3
- v-btn.text-none(color='primary', depressed, @click='deleteSearchUserDialog = true')
- v-icon(left) mdi-clipboard-account
- | Select User...
- .caption.pl-3
- strong ID {{deleteReplaceUser.id}}
- .caption {{deleteReplaceUser.name}}
- em {{deleteReplaceUser.email}}
- v-card-chin
- v-spacer
- v-btn(text, @click='deleteUserDialog = false') {{$t('common:actions.cancel')}}
- v-btn(color='red', dark, @click='deleteUser') {{$t('common:actions.delete')}}
-
- user-search(v-model='deleteSearchUserDialog', @select='assignDeleteUser')
-
-
-
-
-
diff --git a/client/components/admin/admin-users.vue b/client/components/admin/admin-users.vue
deleted file mode 100644
index 52985f5e..00000000
--- a/client/components/admin/admin-users.vue
+++ /dev/null
@@ -1,192 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row, wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-customer.svg', alt='Users', style='width: 80px;')
- .admin-header-title
- .headline.blue--text.text--darken-2.animated.fadeInLeft Users
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s Manage users
- v-spacer
- v-btn.animated.fadeInDown.wait-p2s.mr-3(outlined, color='grey', icon, @click='refresh')
- v-icon mdi-refresh
- v-btn.animated.fadeInDown(color='primary', large, depressed, @click='createUser')
- v-icon(left) mdi-plus
- span New User
- v-card.mt-3.animated.fadeInUp
- .pa-2.d-flex.align-center(:class='$vuetify.theme.dark ? `grey darken-3-d5` : `grey lighten-3`')
- v-text-field(
- solo
- flat
- v-model='search'
- prepend-inner-icon='mdi-account-search-outline'
- label='Search Users...'
- hide-details
- style='max-width: 400px;'
- dense
- )
- v-spacer
- v-select(
- solo
- flat
- hide-details
- label='Identity Provider'
- :items='strategies'
- v-model='filterStrategy'
- item-text='displayName'
- item-value='key'
- style='max-width: 300px;'
- dense
- )
- v-divider
- v-data-table(
- v-model='selected'
- :items='usersFiltered',
- :headers='headers',
- :search='search',
- :page.sync='pagination'
- :items-per-page='15'
- :loading='loading'
- @page-count='pageCount = $event'
- hide-default-footer
- )
- template(slot='item', slot-scope='props')
- tr.is-clickable(:active='props.selected', @click='$router.push("/users/" + props.item.id)')
- //- td
- v-checkbox(hide-details, :input-value='props.selected', color='blue darken-2', @click='props.selected = !props.selected')
- td {{ props.item.id }}
- td: strong {{ props.item.name }}
- td {{ props.item.email }}
- td {{ getStrategyName(props.item.providerKey) }}
- td {{ props.item.createdAt | moment('from') }}
- td
- span(v-if='props.item.lastLoginAt') {{ props.item.lastLoginAt | moment('from') }}
- em.grey--text(v-else) Never
- td.text-right
- v-icon.mr-3(v-if='props.item.isSystem') mdi-lock-outline
- status-indicator(positive, pulse, v-if='props.item.isActive')
- status-indicator(negative, pulse, v-else)
- template(slot='no-data')
- .pa-3
- v-alert.text-left(icon='mdi-alert', outlined, color='grey')
- em.body-2 No users to display!
- v-card-chin(v-if='pageCount > 1')
- v-spacer
- v-pagination(v-model='pagination', :length='pageCount')
- v-spacer
-
- user-create(v-model='isCreateDialogShown', @refresh='refresh(false)')
-
-
-
-
-
diff --git a/client/components/admin/admin-utilities-auth.vue b/client/components/admin/admin-utilities-auth.vue
deleted file mode 100644
index 25b72f79..00000000
--- a/client/components/admin/admin-utilities-auth.vue
+++ /dev/null
@@ -1,93 +0,0 @@
-
- v-card
- v-toolbar(flat, color='primary', dark, dense)
- .subtitle-1 {{ $t('admin:utilities.authTitle') }}
- v-card-text
- .subtitle-1.pb-3.primary--text Generate New Authentication Public / Private Key Certificates
- .body-2 This will invalidate all current session tokens and cause all users to be logged out.
- .body-2.red--text You will need to log back in after the operation.
- v-btn(outlined, color='primary', @click='regenCerts', :disabled='loading').ml-0.mt-3
- v-icon(left) mdi-gesture-double-tap
- span Proceed
- v-divider.my-5
- .subtitle-1.pb-3.primary--text Reset Guest User
- .body-2 This will reset the guest user to its default parameters and permissions.
- v-btn(outlined, color='primary', @click='resetGuest', :disabled='loading').ml-0.mt-3
- v-icon(left) mdi-gesture-double-tap
- span Proceed
-
-
-
-
-
diff --git a/client/components/admin/admin-utilities-cache.vue b/client/components/admin/admin-utilities-cache.vue
deleted file mode 100644
index ac7251b9..00000000
--- a/client/components/admin/admin-utilities-cache.vue
+++ /dev/null
@@ -1,108 +0,0 @@
-
- v-card
- v-toolbar(flat, color='primary', dark, dense)
- .subtitle-1 {{ $t('admin:utilities.cacheTitle') }}
- v-card-text
- .subtitle-1.pb-3.primary--text Flush Pages and Assets Cache
- .body-2 Pages and Assets are cached to disk for better performance. You can flush the cache to force all content to be fetched from the DB again.
- v-btn(outlined, color='primary', @click='flushCache', :disabled='loading').ml-0.mt-3
- v-icon(left) mdi-gesture-double-tap
- span Proceed
- v-divider.my-5
- .subtitle-1.pb-3.primary--text Flush Temporary Uploads
- .body-2 New uploads are temporarily saved to disk while they are being processed. They are automatically deleted after processing, but you can force an immediate cleanup using this tool.
- .body-2.red--text Note that performing this action while an upload is in progress can result in a failed upload.
- v-btn(outlined, color='primary', @click='flushUploads', :disabled='loading').ml-0.mt-3
- v-icon(left) mdi-gesture-double-tap
- span Proceed
- v-divider.my-5
- .subtitle-1.pb-3.primary--text Flush Client-Side Locale Cache
- .body-2 Locale strings are cached in the browser local storage for 24h. You can delete your current cache in order to fetch the latest data during the next page load.
- .body-2 Note that this affects only #[strong your own browser] and not everyone.
- v-btn(outlined, color='primary', @click='flushClientLocaleCache', :disabled='loading').ml-0.mt-3
- v-icon(left) mdi-gesture-double-tap
- span Proceed
-
-
-
-
-
diff --git a/client/components/admin/admin-utilities-content.vue b/client/components/admin/admin-utilities-content.vue
deleted file mode 100644
index ac2b64ca..00000000
--- a/client/components/admin/admin-utilities-content.vue
+++ /dev/null
@@ -1,318 +0,0 @@
-
- v-card
- v-toolbar(flat, color='primary', dark, dense)
- .subtitle-1 {{ $t('admin:utilities.contentTitle') }}
- v-card-text
- .subtitle-1.pb-3.primary--text Rebuild Page Tree
- .body-2 The virtual structure of your wiki is automatically inferred from all page paths. You can trigger a full rebuild of the tree if some virtual folders are missing or not valid anymore.
- v-btn(outlined, color='primary', @click='rebuildTree', :disabled='loading').ml-0.mt-3
- v-icon(left) mdi-gesture-double-tap
- span Proceed
-
- v-divider.my-5
-
- .subtitle-1.pb-3.primary--text Rerender All Pages
- .body-2 All pages will be rendered again. Useful if internal links are broken or the rendering pipeline has changed.
- v-btn(outlined, color='primary', @click='rerenderPages', :disabled='loading', :loading='isRerendering').ml-0.mt-3
- v-icon(left) mdi-gesture-double-tap
- span Proceed
- v-dialog(
- v-model='isRerendering'
- persistent
- max-width='450'
- )
- v-card(color='blue darken-2', dark)
- v-card-text.pa-10.text-center
- semipolar-spinner.animated.fadeIn(
- :animation-duration='1500'
- :size='65'
- color='#FFF'
- style='margin: 0 auto;'
- )
- .mt-5.body-1.white--text Rendering all pages...
- .caption(v-if='renderIndex > 0') Rendering {{renderCurrentPath}}... ({{renderIndex}}/{{renderTotal}}, {{renderProgress}}%)
- .caption.mt-4 Do not leave this page.
- v-progress-linear.mt-5(
- color='white'
- :value='renderProgress'
- stream
- rounded
- :buffer-value='0'
- )
-
- v-divider.my-5
-
- .subtitle-1.pb-3.pl-0.primary--text Migrate all pages to target locale
- .body-2 If you created content before selecting a different locale and activating the namespacing capabilities, you may want to transfer all content to the base locale.
- .body-2.red--text: strong This operation is destructive and cannot be reversed! Make sure you have proper backups!
- v-toolbar.radius-7.mt-5(flat, :color='$vuetify.theme.dark ? `grey darken-3-d5` : `grey lighten-4`', height='80')
- v-select(
- label='Source Locale'
- outlined
- hide-details
- :items='locales'
- item-text='name'
- item-value='code'
- v-model='sourceLocale'
- )
- v-icon.mx-3(large) mdi-chevron-right-box-outline
- v-select(
- label='Target Locale'
- outlined
- hide-details
- :items='locales'
- item-text='name'
- item-value='code'
- v-model='targetLocale'
- )
- .body-2.mt-5 Pages that are already in the target locale will not be touched. If a page already exists at the target, the source page will not be modified as it would create a conflict. If you want to overwrite the target page, you must first delete it.
- v-btn(outlined, color='primary', @click='migrateToLocale', :disabled='loading').ml-0.mt-3
- v-icon(left) mdi-gesture-double-tap
- span Proceed
-
- v-divider.my-5
-
- .subtitle-1.pb-3.pl-0.primary--text Purge Page History
- .body-2 You may want to purge old history for pages to reduce database usage.
- .body-2 This operation only affects the database and not any history saved by a storage module (e.g. git version history)
- v-toolbar.radius-7.mt-5(flat, :color='$vuetify.theme.dark ? `grey darken-3-d5` : `grey lighten-4`', height='80')
- v-select(
- label='Delete history older than...'
- outlined
- hide-details
- :items='purgeHistoryOptions'
- item-text='title'
- item-value='key'
- v-model='purgeHistorySelection'
- )
- v-btn(outlined, color='primary', @click='purgeHistory', :disabled='loading').ml-0.mt-3
- v-icon(left) mdi-gesture-double-tap
- span Proceed
-
-
-
-
-
diff --git a/client/components/admin/admin-utilities-importv1.vue b/client/components/admin/admin-utilities-importv1.vue
deleted file mode 100644
index 78dd3a54..00000000
--- a/client/components/admin/admin-utilities-importv1.vue
+++ /dev/null
@@ -1,507 +0,0 @@
-
- v-card
- v-toolbar(flat, color='primary', dark, dense)
- .subtitle-1 {{ $t('admin:utilities.importv1Title') }}
- v-card-text
- .text-center
- img.animated.fadeInUp.wait-p1s(src='/_assets/svg/icon-software.svg')
- .body-2 Import from Wiki.js 1.x
- v-divider.my-4
- .body-2 Data from a Wiki.js 1.x installation can easily be imported using this tool. What do you want to import?
- v-checkbox(
- label='Content + Uploads'
- value='content'
- color='deep-orange darken-2'
- v-model='importFilters'
- hide-details
- )
- template(v-slot:label)
- strong.deep-orange--text.text--darken-2 Content + Uploads
- .pl-8(v-if='wantContent')
- v-radio-group(v-model='contentMode', hide-details)
- v-radio(
- value='git'
- color='primary'
- )
- template(v-slot:label)
- div
- span Import from Git Connection
- .caption: em #[strong.primary--text Recommended] | The Git storage module will also be configured for you.
- .pl-8.mt-5(v-if='needGit')
- v-row
- v-col(cols='8')
- v-select(
- label='Authentication Mode'
- :items='gitAuthModes'
- v-model='gitAuthMode'
- outlined
- hide-details
- )
- v-col(cols='4')
- v-switch(
- label='Verify SSL Certificate'
- v-model='gitVerifySSL'
- hide-details
- color='primary'
- )
- v-col(cols='8')
- v-text-field(
- outlined
- label='Repository URL'
- :placeholder='(gitAuthMode === `ssh`) ? `e.g. git@github.com:orgname/repo.git` : `e.g. https://github.com/orgname/repo.git`'
- hide-details
- v-model='gitRepoUrl'
- )
- v-col(cols='4')
- v-text-field(
- label='Branch'
- placeholder='e.g. master'
- v-model='gitRepoBranch'
- outlined
- hide-details
- )
- v-col(v-if='gitAuthMode === `ssh`', cols='12')
- v-textarea(
- outlined
- label='Private Key Contents'
- placeholder='-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----'
- hide-details
- v-model='gitPrivKey'
- )
- template(v-else-if='gitAuthMode === `basic`')
- v-col(cols='6')
- v-text-field(
- label='Username'
- v-model='gitUsername'
- outlined
- hide-details
- )
- v-col(cols='6')
- v-text-field(
- type='password'
- label='Password / PAT'
- v-model='gitPassword'
- outlined
- hide-details
- )
- v-col(cols='6')
- v-text-field(
- label='Default Author Email'
- placeholder='e.g. name@company.com'
- v-model='gitUserEmail'
- outlined
- hide-details
- )
- v-col(cols='6')
- v-text-field(
- label='Default Author Name'
- placeholder='e.g. John Smith'
- v-model='gitUserName'
- outlined
- hide-details
- )
- v-col(cols='12')
- v-text-field(
- label='Local Repository Path'
- placeholder='e.g. ./data/repo'
- v-model='gitRepoPath'
- outlined
- hide-details
- )
- .caption.mt-2 This folder should be empty or not exist yet. #[strong.deep-orange--text.text--darken-2 DO NOT] point to your existing Wiki.js 1.x repository folder. In most cases, it should be left to the default value.
- v-alert(color='deep-orange', outlined, icon='mdi-alert', prominent)
- .body-2 - Note that if you already configured the git storage module, its configuration will be replaced with the above.
- .body-2 - Although both v1 and v2 installations can use the same remote git repository, you shouldn't make edits to the same pages simultaneously.
- v-radio-group(v-model='contentMode', hide-details)
- v-divider
- v-radio.mt-3(
- value='disk'
- color='primary'
- )
- template(v-slot:label)
- div
- span Import from local folder
- .caption: em Choose this option only if you didn't have git configured in your Wiki.js 1.x installation.
- .pl-8.mt-5(v-if='needDisk')
- v-text-field(
- outlined
- label='Content Repo Path'
- hint='The absolute path to where the Wiki.js 1.x content is stored on disk.'
- persistent-hint
- v-model='contentPath'
- )
-
- v-checkbox(
- label='Users'
- value='users'
- color='deep-orange darken-2'
- v-model='importFilters'
- hide-details
- )
- template(v-slot:label)
- strong.deep-orange--text.text--darken-2 Users
- .pl-8.mt-5(v-if='wantUsers')
- v-text-field(
- outlined
- label='MongoDB Connection String'
- hint='The connection string to connect to the Wiki.js 1.x MongoDB database.'
- persistent-hint
- v-model='dbConnStr'
- )
- v-radio-group(v-model='groupMode', hide-details, mandatory)
- v-radio(
- value='MULTI'
- color='primary'
- )
- template(v-slot:label)
- div
- span Create groups for each unique user permissions configuration
- .caption: em #[strong.primary--text Recommended] | Users having identical permission sets will be assigned to the same group. Note that this can potentially result in a large amount of groups being created.
- v-divider
- v-radio.mt-3(
- value='SINGLE'
- color='primary'
- )
- template(v-slot:label)
- div
- span Create a single group with all imported users
- .caption: em The new group will have read permissions enabled by default.
- v-divider
- v-radio.mt-3(
- value='NONE'
- color='primary'
- )
- template(v-slot:label)
- div
- span Don't create any group
- .caption: em Users will not be able to access your wiki until they are assigned to a group.
-
- v-alert.mt-5(color='deep-orange', outlined, icon='mdi-alert', prominent)
- .body-2 Note that any user that already exists in this installation will not be imported. A list of skipped users will be displayed upon completion.
- .caption.grey--text You must first delete from this installation any user you want to migrate over from the old installation.
-
- v-card-chin
- v-btn.px-3(depressed, color='deep-orange darken-2', :disabled='!wantUsers && !wantContent', @click='startImport').ml-0
- v-icon(left, color='white') mdi-database-import
- span.white--text Start Import
- v-dialog(
- v-model='isLoading'
- persistent
- max-width='350'
- )
- v-card(color='deep-orange darken-2', dark)
- v-card-text.pa-10.text-center
- semipolar-spinner.animated.fadeIn(
- :animation-duration='1500'
- :size='65'
- color='#FFF'
- style='margin: 0 auto;'
- )
- .mt-5.body-1.white--text Importing from Wiki.js 1.x...
- .caption Please wait
- v-progress-linear.mt-5(
- color='white'
- :value='progress'
- stream
- rounded
- :buffer-value='0'
- )
- v-dialog(
- v-model='isSuccess'
- persistent
- max-width='350'
- )
- v-card(color='green darken-2', dark)
- v-card-text.pa-10.text-center
- v-icon(size='60') mdi-check-circle-outline
- .my-5.body-1.white--text Import completed
- template(v-if='wantUsers')
- .body-2
- span #[strong {{successUsers}}] users imported
- v-btn.text-none.ml-3(
- v-if='failedUsers.length > 0'
- text
- color='white'
- dark
- @click='showFailedUsers = true'
- )
- v-icon(left) mdi-alert
- span {{failedUsers.length}} failed
- .body-2 #[strong {{successGroups}}] groups created
- v-card-actions.green.darken-1
- v-spacer
- v-btn.px-5(
- color='white'
- outlined
- @click='isSuccess = false'
- ) Close
- v-spacer
- v-dialog(
- v-model='showFailedUsers'
- persistent
- max-width='800'
- )
- v-card(color='red darken-2', dark)
- v-toolbar(color='red darken-2', dense)
- v-icon mdi-alert
- .body-2.pl-3 Failed User Imports
- v-spacer
- v-btn.px-5(
- color='white'
- text
- @click='showFailedUsers = false'
- ) Close
- v-simple-table(dense, fixed-header, height='300px')
- template(v-slot:default)
- thead
- tr
- th Provider
- th Email
- th Error
- tbody
- tr(v-for='(fusr, idx) in failedUsers', :key='`fusr-` + idx')
- td {{fusr.provider}}
- td {{fusr.email}}
- td {{fusr.error}}
-
-
-
-
-
diff --git a/client/components/admin/admin-utilities-telemetry.vue b/client/components/admin/admin-utilities-telemetry.vue
deleted file mode 100644
index e36fecef..00000000
--- a/client/components/admin/admin-utilities-telemetry.vue
+++ /dev/null
@@ -1,162 +0,0 @@
-
- v-card
- v-toolbar(flat, color='primary', dark, dense)
- .subtitle-1 {{ $t('admin:utilities.telemetryTitle') }}
- v-form
- v-card-text
- .subtitle-2 What is telemetry?
- .body-2.mt-3 Telemetry allows the developers of Wiki.js to improve the software by collecting basic anonymized data about its usage and the host info. #[br] This is entirely optional and #[strong absolutely no] private data (such as content or personal data) is collected.
- .body-2.mt-3 For maximum privacy, a random client ID is generated during setup. This ID is used to group requests together while keeping complete anonymity. You can reset and generate a new one below at any time.
- v-divider.my-4
- .subtitle-2 What is collected?
- .body-2.mt-3 When telemetry is enabled, only the following data is transmitted:
- v-list
- v-list-item
- v-list-item-avatar: v-icon mdi-information-outline
- v-list-item-content
- v-list-item-title.body-2 Version of Wiki.js installed
- v-list-item-subtitle.caption: em e.g. v2.0.123
- v-list-item
- v-list-item-avatar: v-icon mdi-information-outline
- v-list-item-content
- v-list-item-title.body-2 Basic OS information
- v-list-item-subtitle.caption: em Platform (Linux, macOS or Windows), Total CPU cores and DB type (PostgreSQL, MySQL, MariaDB, SQLite or SQL Server)
- v-list-item
- v-list-item-avatar: v-icon mdi-information-outline
- v-list-item-content
- v-list-item-title.body-2 Crash debug data
- v-list-item-subtitle.caption: em Stack trace of the error
- v-list-item
- v-list-item-avatar: v-icon mdi-information-outline
- v-list-item-content
- v-list-item-title.body-2 Setup analytics
- v-list-item-subtitle.caption: em Installation checkpoint reached
- .body-2 Note that crash debug data is stored for a maximum of 30 days while analytics are stored for a maximum of 16 months, after which it is permanently deleted.
- v-divider.my-4
- .subtitle-2 What is it used for?
- .body-2.mt-3 Telemetry is used by developers to improve Wiki.js, mostly for the following reasons:
- v-list(dense)
- v-list-item
- v-list-item-avatar: v-icon mdi-chevron-right
- v-list-item-content: v-list-item-title: .body-2 Identify critical bugs more easily and fix them in a timely manner.
- v-list-item
- v-list-item-avatar: v-icon mdi-chevron-right
- v-list-item-content: v-list-item-title: .body-2 Understand the upgrade rate of current installations.
- v-list-item
- v-list-item-avatar: v-icon mdi-chevron-right
- v-list-item-content: v-list-item-title: .body-2 Optimize performance and testing scenarios based on most popular environments.
- .body-2 Only authorized developers have access to the data. It is not shared to any 3rd party nor is it used for any other application than improving Wiki.js.
- v-divider.my-4
- .subtitle-2 Settings
- .mt-3
- v-switch.mt-0(
- v-model='telemetry',
- label='Enable Telemetry',
- color='primary',
- hint='Allow Wiki.js to transmit telemetry data.',
- persistent-hint
- )
- v-divider.my-4
- .subtitle-2.mt-3.grey--text.text--darken-1 Client ID
- .body-2.mt-2 {{clientId}}
- v-card-chin
- v-btn.px-3(depressed, color='success', @click='updateTelemetry')
- v-icon(left) mdi-chevron-right
- | Save Changes
- v-spacer
- v-btn.px-3(outlined, color='grey', @click='resetClientId')
- v-icon(left) mdi-autorenew
- span Reset Client ID
-
-
-
-
-
-
diff --git a/client/components/admin/admin-utilities.vue b/client/components/admin/admin-utilities.vue
deleted file mode 100644
index 815177b9..00000000
--- a/client/components/admin/admin-utilities.vue
+++ /dev/null
@@ -1,91 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row, wrap)
- v-flex(xs12)
- .admin-header
- img(src='/_assets/svg/icon-maintenance.svg', alt='Utilities', style='width: 80px;')
- .admin-header-title
- .headline.primary--text {{$t('admin:utilities.title')}}
- .subtitle-1.grey--text {{$t('admin:utilities.subtitle')}}
-
- v-flex(lg3, xs12)
- v-card.animated.fadeInUp
- v-toolbar(flat, color='primary', dark, dense)
- .subtitle-1 {{$t('admin:utilities.tools')}}
- v-list(two-line, dense).py-0
- template(v-for='(tool, idx) in tools')
- v-list-item(:key='tool.key', @click='selectedTool = tool.key', :disabled='!tool.isAvailable')
- v-list-item-avatar
- v-icon(:color='!tool.isAvailable ? `grey lighten-1` : (selectedTool === tool.key ? `blue ` : `grey darken-1`)') {{ tool.icon }}
- v-list-item-content
- v-list-item-title.body-2(:class='!tool.isAvailable ? `grey--text` : (selectedTool === tool.key ? `primary--text` : ``)') {{ $t('admin:utilities.' + tool.i18nKey + 'Title') }}
- v-list-item-subtitle: .caption(:class='!tool.isAvailable ? `grey--text text--lighten-1` : (selectedTool === tool.key ? `blue--text ` : ``)') {{ $t('admin:utilities.' + tool.i18nKey + 'Subtitle') }}
- v-list-item-avatar(v-if='selectedTool === tool.key')
- v-icon.animated.fadeInLeft(color='primary', large) mdi-chevron-right
- v-divider(v-if='idx < tools.length - 1')
-
- v-flex.animated.fadeInUp.wait-p2s(xs12, lg9)
- transition(name='admin-router')
- component(:is='selectedTool')
-
-
-
-
-
-
diff --git a/client/components/admin/admin-webhooks.vue b/client/components/admin/admin-webhooks.vue
deleted file mode 100644
index 84179100..00000000
--- a/client/components/admin/admin-webhooks.vue
+++ /dev/null
@@ -1,116 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row, wrap)
- v-flex(xs12)
- .admin-header
- img.animated.fadeInUp(src='/_assets/svg/icon-winter.svg', alt='Mail', style='width: 80px;')
- .admin-header-title
- .headline.primary--text.animated.fadeInLeft {{ $t('admin:webhooks.title') }}
- .subtitle-1.grey--text.animated.fadeInLeft.wait-p4s {{ $t('admin:webhooks.subtitle') }}
- v-spacer
- v-btn.animated.fadeInDown(color='success', depressed, @click='save', large, disabled)
- v-icon(left) check
- span {{$t('common:actions.apply')}}
-
- v-flex(lg3, xs12)
- v-card.animated.fadeInUp
- v-toolbar(flat, color='primary', dark, dense)
- .subtitle-1 Webhooks
- v-spacer
- v-btn(outline, small)
- v-icon.mr-2 add
- span New
- v-list(two-line, dense).py-0
- template(v-for='(str, idx) in hooks')
- v-list-item(:key='str.key', @click='selectedHook = str.key')
- v-list-item-avatar
- v-icon(color='primary', v-if='str.isEnabled', v-ripple, @click='str.isEnabled = false') check_box
- v-icon(color='grey', v-else, v-ripple, @click='str.isEnabled = true') check_box_outline_blank
- v-list-item-content
- v-list-item-title.body-2(:class='!str.isAvailable ? `grey--text` : (selectedHook === str.key ? `primary--text` : ``)') {{ str.title }}
- v-list-item-sub-title.caption(:class='!str.isAvailable ? `grey--text text--lighten-1` : (selectedHook === str.key ? `blue--text ` : ``)') {{ str.description }}
- v-list-item-avatar(v-if='selectedHook === str.key')
- v-icon.animated.fadeInLeft(color='primary') arrow_forward_ios
- v-divider(v-if='idx < hooks.length - 1')
-
- v-flex(xs12, lg9)
- v-card.wiki-form.animated.fadeInUp.wait-p2s
- v-toolbar(color='primary', dense, flat, dark)
- .subtitle-1 {{hook.title}}
- v-card-text
- v-form
- .authlogo
- img(:src='hook.logo', :alt='hook.title')
- .caption.pt-3 {{hook.description}}
- .caption.pb-3: a(:href='hook.website') {{hook.website}}
- .body-2(v-if='hook.isEnabled')
- span This hook is
-
-
-
-
-
-
diff --git a/client/components/common/loader.vue b/client/components/common/loader.vue
index bf6ca0da..dab216f9 100644
--- a/client/components/common/loader.vue
+++ b/client/components/common/loader.vue
@@ -8,7 +8,7 @@
:size='60'
color='#FFF'
)
- img(v-else-if='mode === `icon`', :src='`/_assets/svg/icon-` + icon + `.svg`', :alt='icon')
+ img(v-else-if='mode === `icon`', :src='`/_assets-legacy/svg/icon-` + icon + `.svg`', :alt='icon')
.subtitle-1.white--text {{ title }}
.caption {{ subtitle }}
diff --git a/client/components/common/nav-header.vue b/client/components/common/nav-header.vue
index d8fc3c80..9dd3addc 100644
--- a/client/components/common/nav-header.vue
+++ b/client/components/common/nav-header.vue
@@ -222,11 +222,11 @@
//- v-list-item-content
//- v-list-item-title {{$t('common:header.myWiki')}}
//- v-list-item-subtitle.overline Coming soon
- v-list-item(href='/p')
+ v-list-item(href='/_profile')
v-list-item-action: v-icon(color='blue-grey') mdi-face-profile
v-list-item-content
v-list-item-title(:class='$vuetify.theme.dark ? `blue-grey--text text--lighten-3` : `blue-grey--text`') {{$t('common:header.profile')}}
- v-list-item(@click='logout')
+ v-list-item(href='/logout')
v-list-item-action: v-icon(color='red') mdi-logout
v-list-item-title.red--text {{$t('common:header.logout')}}
@@ -314,7 +314,7 @@ export default {
url: (this.pictureUrl === 'internal') ? `/_userav/${this.$store.get('user/id')}` : this.pictureUrl
}
} else {
- const nameParts = this.name.toUpperCase().split(' ')
+ const nameParts = ['X', 'X'] // this.name.toUpperCase().split(' ')
let initials = _.head(nameParts).charAt(0)
if (nameParts.length > 1) {
initials += _.last(nameParts).charAt(0)
diff --git a/client/components/common/search-results.vue b/client/components/common/search-results.vue
index 64c57f41..ff9c3e54 100644
--- a/client/components/common/search-results.vue
+++ b/client/components/common/search-results.vue
@@ -2,7 +2,7 @@
.search-results(v-if='searchIsFocused || (search && search.length > 1)')
.search-results-container
.search-results-help(v-if='!search || (search && search.length < 2)')
- img(src='/_assets/svg/icon-search-alt.svg')
+ img(src='/_assets-legacy/svg/icon-search-alt.svg')
.mt-4 {{$t('common:header.searchHint')}}
.search-results-loader(v-else-if='searchIsLoading && (!results || results.length < 1)')
orbit-spinner(
@@ -12,7 +12,7 @@
)
.headline.mt-5 {{$t('common:header.searchLoading')}}
.search-results-none(v-else-if='!searchIsLoading && (!results || results.length < 1)')
- img(src='/_assets/svg/icon-no-results.svg', alt='No Results')
+ img(src='/_assets-legacy/svg/icon-no-results.svg', alt='No Results')
.subheading {{$t('common:header.searchNoResult')}}
template(v-if='search && search.length >= 2 && results && results.length > 0')
v-subheader.white--text {{$t('common:header.searchResultsCount', { total: response.totalHits })}}
@@ -20,7 +20,7 @@
template(v-for='(item, idx) of results')
v-list-item(@click='goToPage(item)', @click.middle="goToPageInNewTab(item)", :key='item.id', :class='idx === cursor ? `highlighted` : ``')
v-list-item-avatar(tile)
- img(src='/_assets/svg/icon-selective-highlighting.svg')
+ img(src='/_assets-legacy/svg/icon-selective-highlighting.svg')
v-list-item-content
v-list-item-title(v-text='item.title')
v-list-item-subtitle.caption(v-text='item.description')
diff --git a/client/components/editor.vue b/client/components/editor.vue
index 15311e9b..170eb1f3 100644
--- a/client/components/editor.vue
+++ b/client/components/editor.vue
@@ -295,7 +295,6 @@ export default {
$content: String!
$description: String!
$editor: String!
- $isPrivate: Boolean!
$isPublished: Boolean!
$locale: String!
$path: String!
@@ -303,35 +302,32 @@ export default {
$publishStartDate: Date
$scriptCss: String
$scriptJs: String
+ $siteId: UUID!
$tags: [String]!
$title: String!
) {
- pages {
- create(
- content: $content
- description: $description
- editor: $editor
- isPrivate: $isPrivate
- isPublished: $isPublished
- locale: $locale
- path: $path
- publishEndDate: $publishEndDate
- publishStartDate: $publishStartDate
- scriptCss: $scriptCss
- scriptJs: $scriptJs
- tags: $tags
- title: $title
- ) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- page {
- id
- updatedAt
- }
+ createPage(
+ content: $content
+ description: $description
+ editor: $editor
+ isPublished: $isPublished
+ locale: $locale
+ path: $path
+ publishEndDate: $publishEndDate
+ publishStartDate: $publishStartDate
+ scriptCss: $scriptCss
+ scriptJs: $scriptJs
+ siteId: $siteId
+ tags: $tags
+ title: $title
+ ) {
+ operation {
+ succeeded
+ message
+ }
+ page {
+ id
+ updatedAt
}
}
}
@@ -341,32 +337,32 @@ export default {
description: this.$store.get('page/description'),
editor: this.$store.get('editor/editorKey'),
locale: this.$store.get('page/locale'),
- isPrivate: false,
isPublished: this.$store.get('page/isPublished'),
path: this.$store.get('page/path'),
publishEndDate: this.$store.get('page/publishEndDate') || '',
publishStartDate: this.$store.get('page/publishStartDate') || '',
scriptCss: this.$store.get('page/scriptCss'),
scriptJs: this.$store.get('page/scriptJs'),
+ siteId: this.$store.get('site/id'),
tags: this.$store.get('page/tags'),
title: this.$store.get('page/title')
}
})
- resp = _.get(resp, 'data.pages.create', {})
- if (_.get(resp, 'responseResult.succeeded')) {
- this.checkoutDateActive = _.get(resp, 'page.updatedAt', this.checkoutDateActive)
+ resp = resp?.data?.createPage || {}
+ if (resp?.operation?.succeeded) {
+ this.checkoutDateActive = resp?.page?.updatedAt ?? this.checkoutDateActive
this.isConflict = false
this.$store.commit('showNotification', {
message: this.$t('editor:save.createSuccess'),
style: 'success',
icon: 'check'
})
- this.$store.set('editor/id', _.get(resp, 'page.id'))
+ this.$store.set('editor/id', resp?.page?.id)
this.$store.set('editor/mode', 'update')
this.exitConfirmed = true
window.location.assign(`/${this.$store.get('page/locale')}/${this.$store.get('page/path')}`)
} else {
- throw new Error(_.get(resp, 'responseResult.message'))
+ throw new Error(resp?.operation?.message)
}
} else {
// --------------------------------------------
@@ -427,7 +423,7 @@ export default {
tags: $tags
title: $title
) {
- responseResult {
+ operation {
succeeded
errorCode
slug
@@ -458,7 +454,7 @@ export default {
}
})
resp = _.get(resp, 'data.pages.update', {})
- if (_.get(resp, 'responseResult.succeeded')) {
+ if (_.get(resp, 'operation.succeeded')) {
this.checkoutDateActive = _.get(resp, 'page.updatedAt', this.checkoutDateActive)
this.isConflict = false
this.$store.commit('showNotification', {
@@ -472,7 +468,7 @@ export default {
}, 1000)
}
} else {
- throw new Error(_.get(resp, 'responseResult.message'))
+ throw new Error(_.get(resp, 'operation.message'))
}
}
diff --git a/client/components/editor/editor-api.vue b/client/components/editor/editor-api.vue
index 14451d53..bf9f6b9a 100644
--- a/client/components/editor/editor-api.vue
+++ b/client/components/editor/editor-api.vue
@@ -59,7 +59,7 @@
v-list-item-group(v-model='kind', mandatory, color='primary')
v-list-item(value='rest')
v-list-item-avatar
- img(src='/_assets/svg/icon-transaction-list.svg', alt='REST')
+ img(src='/_assets-legacy/svg/icon-transaction-list.svg', alt='REST')
v-list-item-content
v-list-item-title REST API
v-list-item-subtitle Classic REST Endpoints
@@ -67,7 +67,7 @@
v-icon(:color='kind === `rest` ? `primary` : `grey lighten-3`') mdi-check-circle
v-list-item(value='graphql', disabled)
v-list-item-avatar
- img(src='/_assets/svg/icon-graphql.svg', alt='GraphQL')
+ img(src='/_assets-legacy/svg/icon-graphql.svg', alt='GraphQL')
v-list-item-content
v-list-item-title GraphQL
v-list-item-subtitle.grey--text.text--lighten-1 Schema-based API
diff --git a/client/components/editor/editor-markdown.vue b/client/components/editor/editor-markdown.vue
index 6134524d..cb8bae3b 100644
--- a/client/components/editor/editor-markdown.vue
+++ b/client/components/editor/editor-markdown.vue
@@ -259,7 +259,7 @@ import tabsetHelper from './markdown/tabset'
const CtrlKey = /Mac/.test(navigator.platform) ? 'Cmd' : 'Ctrl'
// Prism Config
-Prism.plugins.autoloader.languages_path = '/_assets/js/prism/'
+Prism.plugins.autoloader.languages_path = '/_assets-legacy/js/prism/'
Prism.plugins.NormalizeWhitespace.setDefaults({
'remove-trailing': true,
'remove-indent': true,
@@ -378,7 +378,7 @@ md.renderer.rules.katex_block = (tokens, idx) => {
md.renderer.rules.emoji = (token, idx) => {
return twemoji.parse(token[idx].content, {
callback (icon, opts) {
- return `/_assets/svg/twemoji/${icon}.svg`
+ return `/_assets-legacy/svg/twemoji/${icon}.svg`
}
})
}
diff --git a/client/components/editor/editor-modal-editorselect.vue b/client/components/editor/editor-modal-editorselect.vue
index b5ce35f5..9f7e22e0 100644
--- a/client/components/editor/editor-modal-editorselect.vue
+++ b/client/components/editor/editor-modal-editorselect.vue
@@ -6,46 +6,6 @@
.subtitle-1.white--text {{$t('editor:select.title')}}
v-container(grid-list-lg, fluid)
v-layout(row, wrap, justify-center)
- v-flex(xs4)
- v-hover
- template(v-slot:default='{ hover }')
- v-card.radius-7.primary.animated.fadeInUp(
- hover
- light
- ripple
- )
- v-card-text.text-center(@click='')
- img(src='/_assets/svg/editor-icon-api.svg', alt='API', style='width: 36px; opacity: .5;')
- .body-2.blue--text.mt-2.text--lighten-2 API Docs
- .caption.blue--text.text--lighten-1 REST / GraphQL
- v-fade-transition
- v-overlay(
- v-if='hover'
- absolute
- color='primary'
- opacity='.8'
- )
- .body-2.mt-7 Coming Soon
- v-flex(xs4)
- v-hover
- template(v-slot:default='{ hover }')
- v-card.radius-7.primary.animated.fadeInUp.wait-p1s(
- hover
- light
- ripple
- )
- v-card-text.text-center(@click='')
- img(src='/_assets/svg/editor-icon-wikitext.svg', alt='WikiText', style='width: 36px; opacity: .5;')
- .body-2.blue--text.mt-2.text--lighten-2 Blog
- .caption.blue--text.text--lighten-1 Timeline of Posts
- v-fade-transition
- v-overlay(
- v-if='hover'
- absolute
- color='primary'
- opacity='.8'
- )
- .body-2.mt-7 Coming Soon
v-flex(xs4)
v-card.radius-7.animated.fadeInUp.wait-p2s(
hover
@@ -53,7 +13,7 @@
ripple
)
v-card-text.text-center(@click='selectEditor("code")')
- img(src='/_assets/svg/editor-icon-code.svg', alt='Code', style='width: 36px;')
+ img(src='/_assets-legacy/svg/editor-icon-code.svg', alt='Code', style='width: 36px;')
.body-2.primary--text.mt-2 Code
.caption.grey--text Raw HTML
v-flex(xs4)
@@ -63,29 +23,9 @@
ripple
)
v-card-text.text-center(@click='selectEditor("markdown")')
- img(src='/_assets/svg/editor-icon-markdown.svg', alt='Markdown', style='width: 36px;')
+ img(src='/_assets-legacy/svg/editor-icon-markdown.svg', alt='Markdown', style='width: 36px;')
.body-2.primary--text.mt-2 Markdown
.caption.grey--text Plain Text Formatting
- v-flex(xs4)
- v-hover
- template(v-slot:default='{ hover }')
- v-card.radius-7.primary.animated.fadeInUp.wait-p2s(
- hover
- light
- ripple
- )
- v-card-text.text-center(@click='')
- img(src='/_assets/svg/editor-icon-tabular.svg', alt='Tabular', style='width: 36px; opacity: .5;')
- .body-2.blue--text.mt-2.text--lighten-2 Tabular
- .caption.blue--text.text--lighten-1 Excel-like
- v-fade-transition
- v-overlay(
- v-if='hover'
- absolute
- color='primary'
- opacity='.8'
- )
- .body-2.mt-7 Coming Soon
v-flex(xs4)
v-card.radius-7.animated.fadeInUp.wait-p3s(
hover
@@ -93,10 +33,9 @@
ripple
)
v-card-text.text-center(@click='selectEditor("ckeditor")')
- img(src='/_assets/svg/editor-icon-ckeditor.svg', alt='Visual Editor', style='width: 36px;')
+ img(src='/_assets-legacy/svg/editor-icon-ckeditor.svg', alt='Visual Editor', style='width: 36px;')
.body-2.mt-2.primary--text Visual Editor
.caption.grey--text Rich-text WYSIWYG
- //- .caption.blue--text.text--lighten-2 {{$t('editor:select.cannotChange')}}
v-card.radius-7.mt-2(color='teal darken-3', dark)
v-card-text.text-center.py-4
@@ -112,69 +51,9 @@
ripple
)
v-card-text.text-center(@click='fromTemplate')
- img(src='/_assets/svg/icon-cube.svg', alt='From Template', style='width: 42px; opacity: .5;')
+ img(src='/_assets-legacy/svg/icon-cube.svg', alt='From Template', style='width: 42px; opacity: .5;')
.body-2.mt-1.teal--text From Template
.caption.grey--text Use an existing page...
- v-flex(xs4)
- v-hover
- template(v-slot:default='{ hover }')
- v-card.radius-7.teal.animated.fadeInUp.wait-p1s(
- hover
- light
- ripple
- )
- //- v-card-text.text-center(@click='selectEditor("redirect")')
- v-card-text.text-center(@click='')
- img(src='/_assets/svg/icon-route.svg', alt='Redirection', style='width: 42px; opacity: .5;')
- .body-2.mt-1.teal--text.text--lighten-2 Redirection
- .caption.teal--text.text--lighten-1 Redirect the user to...
- v-flex(xs4)
- v-hover
- template(v-slot:default='{ hover }')
- v-card.radius-7.teal.animated.fadeInUp.wait-p2s(
- hover
- light
- ripple
- )
- v-card-text.text-center(@click='')
- img(src='/_assets/svg/icon-sewing-patch.svg', alt='Code', style='width: 42px; opacity: .5;')
- .body-2.mt-1.teal--text.text--lighten-2 Embed
- .caption.teal--text.text--lighten-1 Include external pages
- v-fade-transition
- v-overlay(
- v-if='hover'
- absolute
- color='teal'
- opacity='.8'
- )
- .body-2.mt-7 Coming Soon
- v-hover
- template(v-slot:default='{ hover }')
- v-card.radius-7.mt-2(color='indigo darken-3', dark)
- v-toolbar(dense, flat, color='light-green darken-3')
- v-spacer
- .caption.mr-1 or convert from
- v-btn.mx-1.animated.fadeInUp(depressed, color='light-green darken-2', @click='', disabled)
- v-icon(left) mdi-alpha-a-circle
- .body-2.text-none AsciiDoc
- v-btn.mx-1.animated.fadeInUp.wait-p1s(depressed, color='light-green darken-2', @click='', disabled)
- v-icon(left) mdi-alpha-c-circle
- .body-2.text-none CREOLE
- v-btn.mx-1.animated.fadeInUp.wait-p2s(depressed, color='light-green darken-2', @click='', disabled)
- v-icon(left) mdi-alpha-t-circle
- .body-2.text-none Textile
- v-btn.mx-1.animated.fadeInUp.wait-p3s(depressed, color='light-green darken-2', @click='', disabled)
- v-icon(left) mdi-alpha-w-circle
- .body-2.text-none WikiText
- v-spacer
- v-fade-transition
- v-overlay(
- v-if='hover'
- absolute
- color='light-green darken-3'
- opacity='.8'
- )
- .body-2 Coming Soon
page-selector(mode='select', v-model='templateDialogIsShown', :open-handler='fromTemplateHandle', :path='path', :locale='locale', must-exist)
diff --git a/client/components/login.vue b/client/components/login.vue
deleted file mode 100644
index e7875ccf..00000000
--- a/client/components/login.vue
+++ /dev/null
@@ -1,801 +0,0 @@
-
- v-app
- .login(:style='`background-image: url(` + bgUrl + `);`')
- .login-sd
- .d-flex.mb-5
- .login-logo
- v-avatar(tile, size='34')
- v-img(:src='logoUrl')
- .login-title
- .text-h6.grey--text.text--darken-4 {{ siteTitle }}
- v-alert.mb-0(
- v-model='errorShown'
- transition='slide-y-reverse-transition'
- color='red darken-2'
- tile
- dark
- dense
- icon='mdi-alert'
- )
- .body-2 {{errorMessage}}
- //-------------------------------------------------
- //- PROVIDERS LIST
- //-------------------------------------------------
- template(v-if='screen === `login` && strategies.length > 1')
- .login-subtitle
- .text-subtitle-1 {{$t('auth:selectAuthProvider')}}
- .login-list
- v-list.elevation-1.radius-7(nav, light)
- v-list-item-group(v-model='selectedStrategyKey')
- v-list-item(
- v-for='(stg, idx) of filteredStrategies'
- :key='stg.key'
- :value='stg.key'
- :color='stg.strategy.color'
- )
- v-avatar.mr-3(tile, size='24', v-html='stg.strategy.icon')
- span.text-none {{stg.displayName}}
- //-------------------------------------------------
- //- LOGIN FORM
- //-------------------------------------------------
- template(v-if='screen === `login` && selectedStrategy.strategy.useForm')
- .login-subtitle
- .text-subtitle-1 {{$t('auth:enterCredentials')}}
- .login-form
- v-text-field(
- solo
- flat
- prepend-inner-icon='mdi-clipboard-account'
- background-color='white'
- color='blue darken-2'
- hide-details
- ref='iptEmail'
- v-model='username'
- :placeholder='isUsernameEmail ? $t(`auth:fields.email`) : $t(`auth:fields.username`)'
- :type='isUsernameEmail ? `email` : `text`'
- :autocomplete='isUsernameEmail ? `email` : `username`'
- light
- )
- v-text-field.mt-2(
- solo
- flat
- prepend-inner-icon='mdi-form-textbox-password'
- background-color='white'
- color='blue darken-2'
- hide-details
- ref='iptPassword'
- v-model='password'
- :append-icon='hidePassword ? "mdi-eye-off" : "mdi-eye"'
- @click:append='() => (hidePassword = !hidePassword)'
- :type='hidePassword ? "password" : "text"'
- :placeholder='$t("auth:fields.password")'
- autocomplete='current-password'
- @keyup.enter='login'
- light
- )
- v-btn.mt-2.text-none(
- width='100%'
- large
- color='blue darken-2'
- dark
- @click='login'
- :loading='isLoading'
- ) {{ $t('auth:actions.login') }}
- .text-center.mt-5
- v-btn.text-none(
- text
- rounded
- color='grey darken-3'
- @click.stop.prevent='forgotPassword'
- href='#forgot'
- ): .caption {{ $t('auth:forgotPasswordLink') }}
- v-btn.text-none(
- v-if='selectedStrategyKey === `local` && selectedStrategy.selfRegistration'
- color='indigo darken-2'
- text
- rounded
- href='/register'
- ): .caption {{ $t('auth:switchToRegister.link') }}
- //-------------------------------------------------
- //- FORGOT PASSWORD FORM
- //-------------------------------------------------
- template(v-if='screen === `forgot`')
- .login-subtitle
- .text-subtitle-1 {{$t('auth:forgotPasswordTitle')}}
- .login-info {{ $t('auth:forgotPasswordSubtitle') }}
- .login-form
- v-text-field(
- solo
- flat
- prepend-inner-icon='mdi-clipboard-account'
- background-color='white'
- color='blue darken-2'
- hide-details
- ref='iptForgotPwdEmail'
- v-model='username'
- :placeholder='$t(`auth:fields.email`)'
- type='email'
- autocomplete='email'
- light
- )
- v-btn.mt-2.text-none(
- width='100%'
- large
- color='blue darken-2'
- dark
- @click='forgotPasswordSubmit'
- :loading='isLoading'
- ) {{ $t('auth:sendResetPassword') }}
- .text-center.mt-5
- v-btn.text-none(
- text
- rounded
- color='grey darken-3'
- @click.stop.prevent='screen = `login`'
- href='#forgot'
- ): .caption {{ $t('auth:forgotPasswordCancel') }}
- //-------------------------------------------------
- //- CHANGE PASSWORD FORM
- //-------------------------------------------------
- template(v-if='screen === `changePwd`')
- .login-subtitle
- .text-subtitle-1 {{ $t('auth:changePwd.subtitle') }}
- .login-form
- v-text-field.mt-2(
- type='password'
- solo
- flat
- prepend-inner-icon='mdi-form-textbox-password'
- background-color='white'
- color='blue darken-2'
- hide-details
- ref='iptNewPassword'
- v-model='newPassword'
- :placeholder='$t(`auth:changePwd.newPasswordPlaceholder`)'
- autocomplete='new-password'
- light
- )
- password-strength(slot='progress', v-model='newPassword')
- v-text-field.mt-2(
- type='password'
- solo
- flat
- prepend-inner-icon='mdi-form-textbox-password'
- background-color='white'
- color='blue darken-2'
- hide-details
- v-model='newPasswordVerify'
- :placeholder='$t(`auth:changePwd.newPasswordVerifyPlaceholder`)'
- autocomplete='new-password'
- @keyup.enter='changePassword'
- light
- )
- v-btn.mt-2.text-none(
- width='100%'
- large
- color='blue darken-2'
- dark
- @click='changePassword'
- :loading='isLoading'
- ) {{ $t('auth:changePwd.proceed') }}
-
- //-------------------------------------------------
- //- TFA FORM
- //-------------------------------------------------
- v-dialog(v-model='isTFAShown', max-width='500', persistent)
- v-card
- .login-tfa.text-center.pa-5.grey--text.text--darken-3
- img(src='_assets/svg/icon-pin-pad.svg')
- .subtitle-2 {{$t('auth:tfaFormTitle')}}
- v-text-field.login-tfa-field.mt-2(
- solo
- flat
- background-color='white'
- color='blue darken-2'
- hide-details
- ref='iptTFA'
- v-model='securityCode'
- :placeholder='$t("auth:tfa.placeholder")'
- autocomplete='one-time-code'
- @keyup.enter='verifySecurityCode(false)'
- light
- )
- v-btn.mt-2.text-none(
- width='100%'
- large
- color='blue darken-2'
- dark
- @click='verifySecurityCode(false)'
- :loading='isLoading'
- ) {{ $t('auth:tfa.verifyToken') }}
-
- //-------------------------------------------------
- //- SETUP TFA FORM
- //-------------------------------------------------
- v-dialog(v-model='isTFASetupShown', max-width='600', persistent)
- v-card
- .login-tfa.text-center.pa-5.grey--text.text--darken-3
- .subtitle-1.primary--text {{$t('auth:tfaSetupTitle')}}
- v-divider.my-5
- .subtitle-2 {{$t('auth:tfaSetupInstrFirst')}}
- .caption (#[a(href='https://authy.com/', target='_blank', noopener) Authy], #[a(href='https://support.google.com/accounts/answer/1066447', target='_blank', noopener) Google Authenticator], #[a(href='https://www.microsoft.com/en-us/account/authenticator', target='_blank', noopener) Microsoft Authenticator], etc.)
- .login-tfa-qr.mt-5(v-if='isTFASetupShown', v-html='tfaQRImage')
- .subtitle-2.mt-5 {{$t('auth:tfaSetupInstrSecond')}}
- v-text-field.login-tfa-field.mt-2(
- solo
- flat
- background-color='white'
- color='blue darken-2'
- hide-details
- ref='iptTFASetup'
- v-model='securityCode'
- :placeholder='$t("auth:tfa.placeholder")'
- autocomplete='one-time-code'
- @keyup.enter='verifySecurityCode(true)'
- light
- )
- v-btn.mt-2.text-none(
- width='100%'
- large
- color='blue darken-2'
- dark
- @click='verifySecurityCode(true)'
- :loading='isLoading'
- ) {{ $t('auth:tfa.verifyToken') }}
-
- loader(v-model='isLoading', :color='loaderColor', :title='loaderTitle', :subtitle='$t(`auth:pleaseWait`)')
- notify(style='padding-top: 64px;')
-
-
-
-
-
diff --git a/client/components/new-page.vue b/client/components/new-page.vue
index 71ed92fd..62a8ba63 100644
--- a/client/components/new-page.vue
+++ b/client/components/new-page.vue
@@ -2,7 +2,7 @@
v-app
.newpage
.newpage-content
- img.animated.fadeIn(src='/_assets/svg/icon-delete-file.svg', alt='Not Found')
+ img.animated.fadeIn(src='/_assets-legacy/svg/icon-delete-file.svg', alt='Not Found')
.headline {{ $t('newpage.title') }}
.subtitle-1.mt-3 {{ $t('newpage.subtitle') }}
v-btn.mt-5(:href='`/e/` + locale + `/` + path', x-large)
diff --git a/client/components/profile.vue b/client/components/profile.vue
deleted file mode 100644
index b1206e9c..00000000
--- a/client/components/profile.vue
+++ /dev/null
@@ -1,98 +0,0 @@
-
- v-app(:dark='$vuetify.theme.dark').profile
- nav-header
- v-navigation-drawer.pb-0(v-model='profileDrawerShown', app, fixed, clipped, left, permanent)
- v-list(dense, nav)
- v-list-item(to='/profile', color='primary')
- v-list-item-action: v-icon mdi-face-profile
- v-list-item-content
- v-list-item-title {{$t('profile:title')}}
- //- v-list-item(to='/preferences', disabled)
- //- v-list-item-action: v-icon(color='grey lighten-1') mdi-cog-outline
- //- v-list-item-content
- //- v-list-item-title Preferences
- //- v-list-item-subtitle.caption.grey--text.text--lighten-1 Coming soon
- v-list-item(to='/pages', color='primary')
- v-list-item-action: v-icon mdi-file-document-outline
- v-list-item-content
- v-list-item-title {{$t('profile:pages.title')}}
- //- v-list-item(to='/comments', disabled)
- //- v-list-item-action: v-icon(color='grey lighten-1') mdi-message-reply-text
- //- v-list-item-content
- //- v-list-item-title {{$t('profile:comments.title')}}
- //- v-list-item-subtitle.caption.grey--text.text--lighten-1 Coming soon
-
- v-content(:class='$vuetify.theme.dark ? "grey darken-4" : "grey lighten-5"')
- transition(name='profile-router')
- router-view
-
- nav-footer
- notify
- search-results
-
-
-
-
-
diff --git a/client/components/profile/comments.vue b/client/components/profile/comments.vue
deleted file mode 100644
index 1d5bbfbf..00000000
--- a/client/components/profile/comments.vue
+++ /dev/null
@@ -1,20 +0,0 @@
-
- v-container(fluid, fill-height, grid-list-lg)
- v-layout(row wrap)
- v-flex(xs12)
- .headline.primary--text Comments
- .subheading.grey--text List of comments I posted
-
-
-
-
-
diff --git a/client/components/profile/pages.vue b/client/components/profile/pages.vue
deleted file mode 100644
index 6e37baa7..00000000
--- a/client/components/profile/pages.vue
+++ /dev/null
@@ -1,121 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row wrap)
- v-flex(xs12)
- .profile-header
- img.animated.fadeInUp(src='/_assets/svg/icon-file.svg', alt='Users', style='width: 80px;')
- .profile-header-title
- .headline.primary--text.animated.fadeInLeft {{$t('profile:pages.title')}}
- .subheading.grey--text.animated.fadeInLeft {{$t('profile:pages.subtitle')}}
- v-spacer
- v-btn.animated.fadeInDown.wait-p1s(color='grey', outlined, @click='refresh', large)
- v-icon.grey--text mdi-refresh
- v-flex(xs12)
- v-card.animated.fadeInUp
- v-data-table(
- :items='pages'
- :headers='headers'
- :page.sync='pagination'
- :items-per-page='15'
- :loading='loading'
- must-sort,
- sort-by='updatedAt',
- sort-desc,
- hide-default-footer
- )
- template(slot='item', slot-scope='props')
- tr.is-clickable(:active='props.selected', @click='goToPage(props.item.id)')
- td
- .body-2: strong {{ props.item.title }}
- .caption {{ props.item.description }}
- td.admin-pages-path
- v-chip(label, small, :color='$vuetify.theme.dark ? `grey darken-4` : `grey lighten-4`') {{ props.item.locale }}
- span.ml-2.grey--text(:class='$vuetify.theme.dark ? `text--lighten-1` : `text--darken-2`') / {{ props.item.path }}
- td {{ props.item.createdAt | moment('calendar') }}
- td {{ props.item.updatedAt | moment('calendar') }}
- template(slot='no-data')
- v-alert.ma-3(icon='mdi-alert', :value='true', outlined, color='grey')
- em.caption {{$t('profile:pages.emptyList')}}
- .text-center.py-2.animated.fadeInDown(v-if='this.pageTotal > 1')
- v-pagination(v-model='pagination', :length='pageTotal')
-
-
-
-
-
diff --git a/client/components/profile/profile.vue b/client/components/profile/profile.vue
deleted file mode 100644
index 918cb6d3..00000000
--- a/client/components/profile/profile.vue
+++ /dev/null
@@ -1,923 +0,0 @@
-
- v-container(fluid, grid-list-lg)
- v-layout(row wrap)
- v-flex(xs12)
- .profile-header
- img.animated.fadeInUp(src='/_assets/svg/icon-profile.svg', alt='Users', style='width: 80px;')
- .profile-header-title
- .headline.primary--text.animated.fadeInLeft {{$t('profile:title')}}
- .subheading.grey--text.animated.fadeInLeft {{$t('profile:subtitle')}}
- v-spacer
- v-btn.animated.fadeInDown(color='success', depressed, @click='saveProfile', :loading='saveLoading', large)
- v-icon(left) mdi-check
- span {{$t('common:actions.save')}}
- //- v-btn.animated.fadeInDown(outlined, color='primary', disabled).mr-0
- //- v-icon(left) mdi-earth
- //- span {{$t('profile:viewPublicProfile')}}
- v-flex(lg6 xs12)
- v-card.animated.fadeInUp
- v-toolbar(color='blue-grey', dark, dense, flat)
- v-toolbar-title.subtitle-1 {{$t('profile:myInfo')}}
- v-list(two-line, dense)
- v-list-item
- v-list-item-avatar(size='32')
- v-icon mdi-account
- v-list-item-content
- v-list-item-title {{$t('profile:displayName')}}
- v-list-item-subtitle {{ user.name }}
- v-list-item-action
- v-menu(
- v-model='editPop.name'
- :close-on-content-click='false'
- min-width='350'
- left
- )
- template(v-slot:activator='{ on }')
- v-btn(text, color='grey', small, v-on='on', @click='focusField(`iptDisplayName`)')
- v-icon(left) mdi-pencil
- span {{ $t('common:actions:edit') }}
- v-card
- v-text-field(
- ref='iptDisplayName'
- v-model='user.name'
- :label='$t(`profile:displayName`)'
- solo
- hide-details
- append-icon='mdi-check'
- @click:append='editPop.name = false'
- @keydown.enter='editPop.name = false'
- @keydown.esc='editPop.name = false'
- )
- v-divider
- v-list-item
- v-list-item-avatar(size='32')
- v-icon mdi-map-marker
- v-list-item-content
- v-list-item-title {{$t('profile:location')}}
- v-list-item-subtitle {{ user.location }}
- v-list-item-action
- v-menu(
- v-model='editPop.location'
- :close-on-content-click='false'
- min-width='350'
- left
- )
- template(v-slot:activator='{ on }')
- v-btn(text, color='grey', small, v-on='on', @click='focusField(`iptLocation`)')
- v-icon(left) mdi-pencil
- span {{ $t('common:actions:edit') }}
- v-card
- v-text-field(
- ref='iptLocation'
- v-model='user.location'
- :label='$t(`profile:location`)'
- solo
- hide-details
- append-icon='mdi-check'
- @click:append='editPop.location = false'
- @keydown.enter='editPop.location = false'
- @keydown.esc='editPop.location = false'
- )
- v-divider
- v-list-item
- v-list-item-avatar(size='32')
- v-icon mdi-briefcase
- v-list-item-content
- v-list-item-title {{$t('profile:jobTitle')}}
- v-list-item-subtitle {{ user.jobTitle }}
- v-list-item-action
- v-menu(
- v-model='editPop.jobTitle'
- :close-on-content-click='false'
- min-width='350'
- left
- )
- template(v-slot:activator='{ on }')
- v-btn(text, color='grey', small, v-on='on', @click='focusField(`iptJobTitle`)')
- v-icon(left) mdi-pencil
- span {{ $t('common:actions:edit') }}
- v-card
- v-text-field(
- ref='iptJobTitle'
- v-model='user.jobTitle'
- :label='$t(`profile:jobTitle`)'
- solo
- hide-details
- append-icon='mdi-check'
- @click:append='editPop.jobTitle = false'
- @keydown.enter='editPop.jobTitle = false'
- @keydown.esc='editPop.jobTitle = false'
- )
-
- v-card.mt-3.animated.fadeInUp.wait-p2s
- v-toolbar(color='blue-grey', dark, dense, flat)
- v-toolbar-title
- .subtitle-1 {{$t('profile:auth.title')}}
- v-card-text.pt-0
- v-subheader.pl-0: span.subtitle-2 {{$t('profile:auth.provider')}}
- v-toolbar(
- flat
- :color='$vuetify.theme.dark ? "grey darken-2" : "purple lighten-5"'
- dense
- :class='$vuetify.theme.dark ? "grey--text text--lighten-1" : "purple--text text--darken-4"'
- )
- v-icon(:color='$vuetify.theme.dark ? "grey lighten-1" : "purple darken-4"') mdi-shield-lock
- .subheading.ml-3 {{ user.providerName }}
- //- v-divider.mt-3
- //- v-subheader.pl-0: span.subtitle-2 Two-Factor Authentication (2FA)
- //- .caption.mb-2 2FA adds an extra layer of security by requiring a unique code generated on your smartphone when signing in.
- //- v-btn(color='purple darken-4', disabled).ml-0 Enable 2FA
- //- v-btn(color='purple darken-4', dark, depressed, disabled).ml-0 Disable 2FA
- template(v-if='user.providerKey === `local`')
- v-divider.mt-3
- v-subheader.pl-0: span.subtitle-2 {{$t('profile:auth.changePassword')}}
- v-text-field(
- ref='iptCurrentPass'
- v-model='currentPass'
- outlined
- :label='$t(`profile:auth.currentPassword`)'
- type='password'
- prepend-inner-icon='mdi-form-textbox-password'
- )
- v-text-field(
- ref='iptNewPass'
- v-model='newPass'
- outlined
- :label='$t(`profile:auth.newPassword`)'
- type='password'
- prepend-inner-icon='mdi-form-textbox-password'
- autocomplete='off'
- counter='255'
- loading
- )
- password-strength(slot='progress', v-model='newPass')
- v-text-field(
- ref='iptVerifyPass'
- v-model='verifyPass'
- outlined
- :label='$t(`profile:auth.verifyPassword`)'
- type='password'
- prepend-inner-icon='mdi-form-textbox-password'
- autocomplete='off'
- hide-details
- )
- v-card-chin(v-if='user.providerKey === `local`')
- v-spacer
- v-btn.px-4(color='purple darken-4', dark, depressed, @click='changePassword', :loading='changePassLoading')
- v-icon(left) mdi-progress-check
- span {{$t('profile:auth.changePassword')}}
- v-flex(lg6 xs12)
- //- v-card
- //- v-toolbar(color='blue-grey', dark, dense, flat)
- //- v-toolbar-title
- //- .subtitle-1 Picture
- //- v-card-title
- //- v-avatar.blue(v-if='picture.kind === `initials`', :size='40')
- //- span.white--text.subheading {{picture.initials}}
- //- v-avatar(v-else-if='picture.kind === `image`', :size='40')
- //- v-img(:src='picture.url')
- //- v-btn(outlined).mx-4 Upload Picture
- //- v-btn(outlined, disabled) Remove Picture
- v-card.animated.fadeInUp.wait-p2s
- v-toolbar(color='blue-grey', dark, dense, flat)
- v-toolbar-title.subtitle-1 {{$t('profile:preferences')}}
- v-list(two-line, dense)
- v-list-item
- v-list-item-avatar(size='32')
- v-icon mdi-map-clock-outline
- v-list-item-content
- v-list-item-title {{$t('profile:timezone')}}
- v-list-item-subtitle {{ user.timezone }}
- v-list-item-action
- v-menu(
- v-model='editPop.timezone'
- :close-on-content-click='false'
- min-width='350'
- max-width='350'
- left
- )
- template(v-slot:activator='{ on }')
- v-btn(text, color='grey', small, v-on='on', @click='focusField(`iptTimezone`)')
- v-icon(left) mdi-pencil
- span {{ $t('common:actions:edit') }}
- v-card(flat)
- v-select(
- ref='iptTimezone'
- :items='timezones'
- v-model='user.timezone'
- :label='$t(`profile:timezone`)'
- solo
- flat
- dense
- hide-details
- @keydown.enter='editPop.timezone = false'
- @keydown.esc='editPop.timezone = false'
- style='height: 38px;'
- )
- v-card-chin
- v-spacer
- v-btn(
- small
- text
- color='primary'
- @click='editPop.timezone = false'
- )
- v-icon(left) mdi-check
- span {{$t('common:actions.ok')}}
- v-divider
- v-list-item
- v-list-item-avatar(size='32')
- v-icon mdi-calendar-month-outline
- v-list-item-content
- v-list-item-title {{$t('profile:dateFormat')}}
- v-list-item-subtitle {{ user.dateFormat && user.dateFormat.length > 0 ? user.dateFormat : $t('profile:localeDefault') }}
- v-list-item-action
- v-menu(
- v-model='editPop.dateFormat'
- :close-on-content-click='false'
- min-width='350'
- max-width='350'
- left
- )
- template(v-slot:activator='{ on }')
- v-btn(text, color='grey', small, v-on='on', @click='focusField(`iptDateFormat`)')
- v-icon(left) mdi-pencil
- span {{ $t('common:actions:edit') }}
- v-card(flat)
- v-select(
- ref='iptDateFormat'
- :items='dateFormats'
- v-model='user.dateFormat'
- :label='$t(`profile:dateFormat`)'
- solo
- flat
- dense
- hide-details
- @keydown.enter='editPop.dateFormat = false'
- @keydown.esc='editPop.dateFormat = false'
- style='height: 38px;'
- )
- v-card-chin
- v-spacer
- v-btn(
- small
- text
- color='primary'
- @click='editPop.dateFormat = false'
- )
- v-icon(left) mdi-check
- span {{$t('common:actions.ok')}}
- v-divider
- v-list-item
- v-list-item-avatar(size='32')
- v-icon mdi-palette
- v-list-item-content
- v-list-item-title {{$t('profile:appearance')}}
- v-list-item-subtitle {{ currentAppearance }}
- v-list-item-action
- v-menu(
- v-model='editPop.appearance'
- :close-on-content-click='false'
- min-width='350'
- max-width='350'
- left
- )
- template(v-slot:activator='{ on }')
- v-btn(text, color='grey', small, v-on='on', @click='focusField(`iptAppearance`)')
- v-icon(left) mdi-pencil
- span {{ $t('common:actions:edit') }}
- v-card(flat)
- v-select(
- ref='iptAppearance'
- :items='appearances'
- v-model='user.appearance'
- :label='$t(`profile:appearance`)'
- solo
- flat
- dense
- hide-details
- @keydown.enter='editPop.appearance = false'
- @keydown.esc='editPop.appearance = false'
- style='height: 38px;'
- )
- v-card-chin
- v-spacer
- v-btn(
- small
- text
- color='primary'
- @click='editPop.appearance = false'
- )
- v-icon(left) mdi-check
- span {{$t('common:actions.ok')}}
-
- v-card.mt-3.animated.fadeInUp.wait-p3s
- v-toolbar(color='primary', dark, dense, flat)
- v-toolbar-title
- .subtitle-1 {{$t('profile:groups.title')}}
- v-list(dense)
- template(v-for='(grp, idx) of user.groups')
- v-list-item(:key='`grp-id-` + grp')
- v-list-item-avatar(size='32')
- v-icon mdi-account-group
- v-list-item-content
- v-list-item-title.body-2 {{grp}}
- v-divider(v-if='idx < user.groups.length - 1')
-
- v-card.mt-3.animated.fadeInUp.wait-p4s
- v-toolbar(color='teal', dark, dense, flat)
- v-toolbar-title
- .subtitle-1 {{$t('profile:activity.title')}}
- v-card-text.grey--text.text--darken-2
- .caption.grey--text {{$t('profile:activity.joinedOn')}}
- .body-2: strong {{ user.createdAt | moment('LLLL') }}
- .caption.grey--text.mt-3 {{$t('profile:activity.lastUpdatedOn')}}
- .body-2: strong {{ user.updatedAt | moment('LLLL') }}
- .caption.grey--text.mt-3 {{$t('profile:activity.lastLoginOn')}}
- .body-2: strong {{ user.lastLoginAt | moment('LLLL') }}
- v-divider.mt-3
- .caption.grey--text.mt-3 {{$t('profile:activity.pagesCreated')}}
- .body-2: strong {{ user.pagesTotal }}
- .caption.grey--text.mt-3 {{$t('profile:activity.commentsPosted')}}
- .body-2: strong 0
-
-
-
-
-
diff --git a/client/components/register.vue b/client/components/register.vue
deleted file mode 100644
index d64cc15e..00000000
--- a/client/components/register.vue
+++ /dev/null
@@ -1,306 +0,0 @@
-
- v-app
- .register
- v-container(grid-list-lg)
- v-layout(row, wrap)
- v-flex(
- xs12
- offset-sm1, sm10
- offset-md2, md8
- offset-lg3, lg6
- offset-xl4, xl4
- )
- transition(name='fadeUp')
- v-card.elevation-5.md2(v-show='isShown')
- v-toolbar(color='indigo', flat, dense, dark)
- v-spacer
- .subheading {{ $t('auth:registerTitle') }}
- v-spacer
- v-card-text.text-center
- h1.display-1.indigo--text.py-2 {{ siteTitle }}
- .body-2 {{ $t('auth:registerSubTitle') }}
- v-text-field.md2.mt-3(
- solo
- flat
- prepend-icon='mdi-email'
- :background-color='$vuetify.theme.dark ? `grey darken-3` : `grey lighten-4`'
- hide-details
- ref='iptEmail'
- v-model='email'
- :placeholder='$t("auth:fields.email")'
- color='indigo'
- )
- v-text-field.md2.mt-2(
- solo
- flat
- prepend-icon='mdi-form-textbox-password'
- :background-color='$vuetify.theme.dark ? `grey darken-3` : `grey lighten-4`'
- ref='iptPassword'
- v-model='password'
- :append-icon='hidePassword ? "mdi-eye-off" : "mdi-eye"'
- @click:append='() => (hidePassword = !hidePassword)'
- :type='hidePassword ? "password" : "text"'
- :placeholder='$t("auth:fields.password")'
- color='indigo'
- loading
- counter='255'
- )
- password-strength(slot='progress', v-model='password')
- v-text-field.md2.mt-2(
- solo
- flat
- prepend-icon='mdi-form-textbox-password'
- :background-color='$vuetify.theme.dark ? `grey darken-3` : `grey lighten-4`'
- hide-details
- ref='iptVerifyPassword'
- v-model='verifyPassword'
- @click:append='() => (hidePassword = !hidePassword)'
- type='password'
- :placeholder='$t("auth:fields.verifyPassword")'
- color='indigo'
- )
- v-text-field.md2.mt-2(
- solo
- flat
- prepend-icon='mdi-account'
- :background-color='$vuetify.theme.dark ? `grey darken-3` : `grey lighten-4`'
- ref='iptName'
- v-model='name'
- :placeholder='$t("auth:fields.name")'
- @keyup.enter='register'
- color='indigo'
- counter='255'
- )
- v-card-actions.pb-4
- v-spacer
- v-btn.md2(
- width='100%'
- max-width='250px'
- large
- dark
- color='indigo'
- @click='register'
- rounded
- :loading='isLoading'
- ) {{ $t('auth:actions.register') }}
- v-spacer
- v-divider
- v-card-actions.py-3.grey(:class='$vuetify.theme.dark ? `darken-4-l1` : `lighten-4`')
- v-spacer
- i18next.caption(path='auth:switchToLogin.text', tag='div')
- a.caption(href='/login', place='link') {{ $t('auth:switchToLogin.link') }}
- v-spacer
-
- loader(v-model='isLoading', :mode='loaderMode', :icon='loaderIcon', :color='loaderColor', :title='loaderTitle', :subtitle='loaderSubtitle')
- nav-footer(color='grey darken-5', dark-color='grey darken-5')
- notify(style='padding-top: 64px;')
-
-
-
-
-
diff --git a/client/components/setup.vue b/client/components/setup.vue
deleted file mode 100644
index 012e4667..00000000
--- a/client/components/setup.vue
+++ /dev/null
@@ -1,273 +0,0 @@
-
- v-app.setup
- v-content
- v-container
- v-layout
- v-flex(xs12, lg6, offset-lg3)
- v-card.elevation-20.radius-7.animated.fadeInUp
- v-alert(v-if='isDevMode', tile, dark, color='red darken-3', icon='mdi-alert', prominent)
- .body-2 You are running an unstable, unreleased development version. This code base is #[strong NOT] for production use!
- .body-2.mt-3 Cloning the dev branch directly from GitHub is #[strong NOT] the proper way to install Wiki.js!
- .body-2 Read the #[a(href='https://docs.requarks.io/install', style='color: #FFF;') documentation] on correctly installing the latest stable version.
- .text-center
- img.setup-logo.animated.fadeInUp.wait-p2s(src='/_assets/svg/logo-wikijs-full.svg', alt='Wiki.js Logo')
- v-alert(v-model='error', type='error', icon='mdi-alert', tile, dismissible) {{ errorMessage }}
- v-alert(v-if='!error', tile, color='blue lighten-5', :value='true')
- v-icon.mr-3(color='blue') mdi-package-variant
- span.blue--text You are about to install Wiki.js #[strong {{wikiVersion}}].
- v-card-text
- .overline.pl-3 Administrator Account
- v-container.pa-3.mt-3(grid-list-xl)
- v-layout(row, wrap)
- v-flex(xs12)
- v-text-field(
- outlined
- v-model='conf.adminEmail',
- label='Administrator Email',
- hint='The email address of the administrator account.',
- persistent-hint
- required
- ref='adminEmailInput'
- )
- v-flex(xs6)
- v-text-field(
- outlined
- ref='adminPassword',
- counter='255'
- v-model='conf.adminPassword',
- label='Password',
- :append-icon="pwdMode ? 'mdi-eye-off' : 'mdi-eye'"
- @click:append="() => (pwdMode = !pwdMode)"
- :type="pwdMode ? 'password' : 'text'"
- hint='At least 8 characters long.',
- persistent-hint
- )
- v-flex(xs6)
- v-text-field(
- outlined
- ref='adminPasswordConfirm',
- counter='255'
- v-model='conf.adminPasswordConfirm',
- label='Confirm Password',
- :append-icon="pwdConfirmMode ? 'mdi-eye-off' : 'mdi-eye'"
- @click:append="() => (pwdConfirmMode = !pwdConfirmMode)"
- :type="pwdConfirmMode ? 'password' : 'text'"
- hint='Verify your password again.',
- persistent-hint
- )
- v-divider.mb-4
- .overline.pl-3.mb-5 Site URL
- v-text-field.mb-4.mx-3(
- outlined
- ref='adminSiteUrl',
- v-model='conf.siteUrl',
- label='Site URL',
- hint='Full URL to your wiki, without the trailing slash (e.g. https://wiki.example.com). This should be the public facing URL, not the internal one if using a reverse-proxy.',
- persistent-hint
- @keyup.enter='install'
- )
- v-divider.mb-4
- .overline.pl-3.mb-3 Telemetry
- v-switch.ml-3(
- inset
- color='primary',
- v-model='conf.telemetry',
- label='Allow Telemetry',
- persistent-hint,
- hint='Help Wiki.js developers improve this app with anonymized telemetry.'
- )
- a.pl-3(style='font-size: 12px; letter-spacing: initial;', href='https://docs.requarks.io/telemetry', target='_blank') Learn more
- v-divider.mt-2
- v-card-actions
- v-btn(color='primary', @click='install', :disabled='loading', x-large, depressed, block)
- v-icon(left) mdi-check
- span Install
-
- v-dialog(v-model='loading', width='450', persistent)
- v-card(color='primary', dark).radius-7
- v-card-text.text-center.py-5
- .py-3(style='width: 64px; display:inline-block;')
- breeding-rhombus-spinner(
- :animation-duration='2000'
- :size='64'
- color='#FFF'
- )
- template(v-if='!success')
- .subtitle-1.white--text Finalizing your installation...
- .caption Just a moment
- template(v-else)
- .subtitle-1.white--text Installation complete!
- .caption Redirecting...
-
-
-
-
-
diff --git a/client/components/tags.vue b/client/components/tags.vue
index 3d717b38..6865d0ce 100644
--- a/client/components/tags.vue
+++ b/client/components/tags.vue
@@ -89,7 +89,7 @@
v-btn(text, height='40'): v-icon(size='20') mdi-chevron-double-down
v-divider
.text-center.pt-10(v-if='selection.length < 1')
- img(src='/_assets/svg/icon-price-tag.svg')
+ img(src='/_assets-legacy/svg/icon-price-tag.svg')
.subtitle-2.grey--text {{$t('tags:selectOneMoreTagsHint')}}
.px-5.py-2(v-else)
v-data-iterator(
@@ -112,11 +112,11 @@
.subtitle-2.grey--text.mt-5 {{$t('tags:retrievingResultsLoading')}}
template(v-slot:no-data)
.text-center.pt-10
- img(src='/_assets/svg/icon-info.svg')
+ img(src='/_assets-legacy/svg/icon-info.svg')
.subtitle-2.grey--text {{$t('tags:noResults')}}
template(v-slot:no-results)
.text-center.pt-10
- img(src='/_assets/svg/icon-info.svg')
+ img(src='/_assets-legacy/svg/icon-info.svg')
.subtitle-2.grey--text {{$t('tags:noResultsWithFilter')}}
template(v-slot:default='props')
v-row(align='stretch')
diff --git a/client/components/welcome.vue b/client/components/welcome.vue
deleted file mode 100644
index c182efdd..00000000
--- a/client/components/welcome.vue
+++ /dev/null
@@ -1,35 +0,0 @@
-
- v-app
- .onboarding
- .onboarding-content
- img.animated.fadeIn(src='/_assets-legacy/svg/logo-wikijs.svg', alt='Wiki.js')
- .headline.animated.fadeInUp {{ $t('welcome.title') }}
- .subtitle-1.mt-3.animated.fadeInUp.wait-p1s {{ $t('welcome.subtitle') }}
- div
- v-btn.mt-5.mx-3.animated.fadeInUp.wait-p2s(color='primary', :href='`/e/` + locale + `/home`', x-large)
- v-icon(left) mdi-plus
- span {{ $t('welcome.createhome') }}
- v-btn.mt-5.mx-3.animated.fadeInUp.wait-p3s(color='primary', href='/a', x-large)
- v-icon(left) mdi-view-dashboard
- span {{ $t('welcome.goadmin') }}
-
-
-
-
-
-
diff --git a/client/graph/admin/analytics/analytics-mutation-save-providers.gql b/client/graph/admin/analytics/analytics-mutation-save-providers.gql
deleted file mode 100644
index 09cc9a49..00000000
--- a/client/graph/admin/analytics/analytics-mutation-save-providers.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation($providers: [AnalyticsProviderInput]!) {
- analytics {
- updateProviders(providers: $providers) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/analytics/analytics-query-providers.gql b/client/graph/admin/analytics/analytics-query-providers.gql
deleted file mode 100644
index 4402fc9c..00000000
--- a/client/graph/admin/analytics/analytics-query-providers.gql
+++ /dev/null
@@ -1,17 +0,0 @@
-query {
- analytics {
- providers {
- isEnabled
- key
- title
- description
- isAvailable
- logo
- website
- config {
- key
- value
- }
- }
- }
-}
diff --git a/client/graph/admin/auth/auth-query-groups.gql b/client/graph/admin/auth/auth-query-groups.gql
deleted file mode 100644
index c4bd890f..00000000
--- a/client/graph/admin/auth/auth-query-groups.gql
+++ /dev/null
@@ -1,8 +0,0 @@
-query {
- groups {
- list {
- id
- name
- }
- }
-}
diff --git a/client/graph/admin/auth/auth-query-host.gql b/client/graph/admin/auth/auth-query-host.gql
deleted file mode 100644
index b9c7e31d..00000000
--- a/client/graph/admin/auth/auth-query-host.gql
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- site {
- config {
- host
- }
- }
-}
diff --git a/client/graph/admin/auth/auth-query-strategies.gql b/client/graph/admin/auth/auth-query-strategies.gql
deleted file mode 100644
index ce2ed90a..00000000
--- a/client/graph/admin/auth/auth-query-strategies.gql
+++ /dev/null
@@ -1,21 +0,0 @@
-query {
- authentication {
- strategies {
- isEnabled
- key
- title
- description
- isAvailable
- useForm
- logo
- website
- config {
- key
- value
- }
- selfRegistration
- domainWhitelist
- autoEnrollGroups
- }
- }
-}
diff --git a/client/graph/admin/contribute/contribute-query-contributors.gql b/client/graph/admin/contribute/contribute-query-contributors.gql
deleted file mode 100644
index d5c4b4ec..00000000
--- a/client/graph/admin/contribute/contribute-query-contributors.gql
+++ /dev/null
@@ -1,17 +0,0 @@
-query {
- contribute {
- contributors {
- company
- currency
- description
- id
- image
- name
- profile
- tier
- totalDonated
- twitter
- website
- }
- }
-}
diff --git a/client/graph/admin/dashboard/dashboard-query-stats.gql b/client/graph/admin/dashboard/dashboard-query-stats.gql
deleted file mode 100644
index 75bf2096..00000000
--- a/client/graph/admin/dashboard/dashboard-query-stats.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-query {
- system {
- info {
- currentVersion
- latestVersion
- groupsTotal
- pagesTotal
- usersTotal
- tagsTotal
- }
- }
-}
diff --git a/client/graph/admin/dev/dev-mutation-save-flags.gql b/client/graph/admin/dev/dev-mutation-save-flags.gql
deleted file mode 100644
index be9a9ffd..00000000
--- a/client/graph/admin/dev/dev-mutation-save-flags.gql
+++ /dev/null
@@ -1,16 +0,0 @@
-mutation (
- $flags: [SystemFlagInput]!
-) {
- system {
- updateFlags(
- flags: $flags
- ) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/dev/dev-query-flags.gql b/client/graph/admin/dev/dev-query-flags.gql
deleted file mode 100644
index 3650b06f..00000000
--- a/client/graph/admin/dev/dev-query-flags.gql
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- system {
- flags {
- key
- value
- }
- }
-}
diff --git a/client/graph/admin/groups/groups-mutation-assign.gql b/client/graph/admin/groups/groups-mutation-assign.gql
deleted file mode 100644
index af76cb5e..00000000
--- a/client/graph/admin/groups/groups-mutation-assign.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation ($groupId: Int!, $userId: Int!) {
- groups {
- assignUser(groupId: $groupId, userId: $userId) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/groups/groups-mutation-create.gql b/client/graph/admin/groups/groups-mutation-create.gql
deleted file mode 100644
index 281a3fe8..00000000
--- a/client/graph/admin/groups/groups-mutation-create.gql
+++ /dev/null
@@ -1,18 +0,0 @@
-mutation ($name: String!) {
- groups {
- create(name: $name) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- group {
- id
- name
- createdAt
- updatedAt
- }
- }
- }
-}
diff --git a/client/graph/admin/groups/groups-mutation-unassign.gql b/client/graph/admin/groups/groups-mutation-unassign.gql
deleted file mode 100644
index 7cc2cb70..00000000
--- a/client/graph/admin/groups/groups-mutation-unassign.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation ($groupId: Int!, $userId: Int!) {
- groups {
- unassignUser(groupId: $groupId, userId: $userId) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/groups/groups-query-list.gql b/client/graph/admin/groups/groups-query-list.gql
deleted file mode 100644
index c2453f10..00000000
--- a/client/graph/admin/groups/groups-query-list.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-query {
- groups {
- list {
- id
- name
- isSystem
- userCount
- createdAt
- updatedAt
- }
- }
-}
diff --git a/client/graph/admin/locale/locale-mutation-download.gql b/client/graph/admin/locale/locale-mutation-download.gql
deleted file mode 100644
index e11c0613..00000000
--- a/client/graph/admin/locale/locale-mutation-download.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation($locale: String!) {
- localization {
- downloadLocale(locale: $locale) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/locale/locale-mutation-save.gql b/client/graph/admin/locale/locale-mutation-save.gql
deleted file mode 100644
index df38c1a2..00000000
--- a/client/graph/admin/locale/locale-mutation-save.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation($locale: String!, $autoUpdate: Boolean!, $namespacing: Boolean!, $namespaces: [String]!) {
- localization {
- updateLocale(locale: $locale, autoUpdate: $autoUpdate, namespacing: $namespacing, namespaces: $namespaces) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/locale/locale-query-list.gql b/client/graph/admin/locale/locale-query-list.gql
deleted file mode 100644
index c91e2d90..00000000
--- a/client/graph/admin/locale/locale-query-list.gql
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- localization {
- locales {
- availability
- code
- createdAt
- isInstalled
- installDate
- isRTL
- name
- nativeName
- updatedAt
- }
- config {
- locale
- autoUpdate
- namespacing
- namespaces
- }
- }
-}
diff --git a/client/graph/admin/logging/logging-mutation-save-loggers.gql b/client/graph/admin/logging/logging-mutation-save-loggers.gql
deleted file mode 100644
index 2d86ba14..00000000
--- a/client/graph/admin/logging/logging-mutation-save-loggers.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation($loggers: [LoggerInput]) {
- logging {
- updateLoggers(loggers: $loggers) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/logging/logging-query-loggers.gql b/client/graph/admin/logging/logging-query-loggers.gql
deleted file mode 100644
index 3c244c32..00000000
--- a/client/graph/admin/logging/logging-query-loggers.gql
+++ /dev/null
@@ -1,17 +0,0 @@
-query {
- logging {
- loggers(orderBy: "title ASC") {
- isEnabled
- key
- title
- description
- logo
- website
- level
- config {
- key
- value
- }
- }
- }
-}
diff --git a/client/graph/admin/logging/logging-subscription-livetrail.gql b/client/graph/admin/logging/logging-subscription-livetrail.gql
deleted file mode 100644
index 80e1fda4..00000000
--- a/client/graph/admin/logging/logging-subscription-livetrail.gql
+++ /dev/null
@@ -1,7 +0,0 @@
-subscription {
- loggingLiveTrail {
- level
- output
- timestamp
- }
-}
diff --git a/client/graph/admin/mail/mail-mutation-save-config.gql b/client/graph/admin/mail/mail-mutation-save-config.gql
deleted file mode 100644
index 3b8f6999..00000000
--- a/client/graph/admin/mail/mail-mutation-save-config.gql
+++ /dev/null
@@ -1,38 +0,0 @@
-mutation (
- $senderName: String!,
- $senderEmail: String!,
- $host: String!,
- $port: Int!,
- $secure: Boolean!,
- $verifySSL: Boolean!,
- $user: String!,
- $pass: String!,
- $useDKIM: Boolean!,
- $dkimDomainName: String!,
- $dkimKeySelector: String!,
- $dkimPrivateKey: String!
-) {
- mail {
- updateConfig(
- senderName: $senderName,
- senderEmail: $senderEmail,
- host: $host,
- port: $port,
- secure: $secure,
- verifySSL: $verifySSL,
- user: $user,
- pass: $pass,
- useDKIM: $useDKIM,
- dkimDomainName: $dkimDomainName,
- dkimKeySelector: $dkimKeySelector,
- dkimPrivateKey: $dkimPrivateKey
- ) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/mail/mail-mutation-sendtest.gql b/client/graph/admin/mail/mail-mutation-sendtest.gql
deleted file mode 100644
index 66157947..00000000
--- a/client/graph/admin/mail/mail-mutation-sendtest.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation ($recipientEmail: String!) {
- mail {
- sendTest(recipientEmail: $recipientEmail) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/mail/mail-query-config.gql b/client/graph/admin/mail/mail-query-config.gql
deleted file mode 100644
index 66232acb..00000000
--- a/client/graph/admin/mail/mail-query-config.gql
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- mail {
- config {
- senderName
- senderEmail
- host
- port
- secure
- verifySSL
- user
- pass
- useDKIM
- dkimDomainName
- dkimKeySelector
- dkimPrivateKey
- }
- }
-}
diff --git a/client/graph/admin/pages/pages-query-list.gql b/client/graph/admin/pages/pages-query-list.gql
deleted file mode 100644
index 0faf0785..00000000
--- a/client/graph/admin/pages/pages-query-list.gql
+++ /dev/null
@@ -1,17 +0,0 @@
-query {
- pages {
- list {
- id
- locale
- path
- title
- description
- contentType
- isPublished
- isPrivate
- privateNS
- createdAt
- updatedAt
- }
- }
-}
diff --git a/client/graph/admin/pages/pages-query-single.gql b/client/graph/admin/pages/pages-query-single.gql
deleted file mode 100644
index f245772a..00000000
--- a/client/graph/admin/pages/pages-query-single.gql
+++ /dev/null
@@ -1,27 +0,0 @@
-query($id: Int!) {
- pages {
- single(id:$id) {
- id
- path
- hash
- title
- description
- isPrivate
- isPublished
- privateNS
- publishStartDate
- publishEndDate
- contentType
- createdAt
- updatedAt
- editor
- locale
- authorId
- authorName
- authorEmail
- creatorId
- creatorName
- creatorEmail
- }
- }
-}
diff --git a/client/graph/admin/rendering/rendering-mutation-save-renderers.gql b/client/graph/admin/rendering/rendering-mutation-save-renderers.gql
deleted file mode 100644
index 8a717785..00000000
--- a/client/graph/admin/rendering/rendering-mutation-save-renderers.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation($renderers: [RendererInput]) {
- rendering {
- updateRenderers(renderers: $renderers) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/rendering/rendering-query-renderers.gql b/client/graph/admin/rendering/rendering-query-renderers.gql
deleted file mode 100644
index b117b7ab..00000000
--- a/client/graph/admin/rendering/rendering-query-renderers.gql
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- rendering {
- renderers {
- isEnabled
- key
- title
- description
- icon
- dependsOn
- input
- output
- config {
- key
- value
- }
- }
- }
-}
diff --git a/client/graph/admin/search/search-mutation-rebuild-index.gql b/client/graph/admin/search/search-mutation-rebuild-index.gql
deleted file mode 100644
index 9f4a46ee..00000000
--- a/client/graph/admin/search/search-mutation-rebuild-index.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation {
- search {
- rebuildIndex {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/search/search-mutation-save-engines.gql b/client/graph/admin/search/search-mutation-save-engines.gql
deleted file mode 100644
index 7ab64db6..00000000
--- a/client/graph/admin/search/search-mutation-save-engines.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation($engines: [SearchEngineInput]) {
- search {
- updateSearchEngines(engines: $engines) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/search/search-query-engines.gql b/client/graph/admin/search/search-query-engines.gql
deleted file mode 100644
index bc305a09..00000000
--- a/client/graph/admin/search/search-query-engines.gql
+++ /dev/null
@@ -1,17 +0,0 @@
-query {
- search {
- searchEngines(orderBy: "title") {
- isEnabled
- key
- title
- description
- logo
- website
- isAvailable
- config {
- key
- value
- }
- }
- }
-}
diff --git a/client/graph/admin/storage/storage-mutation-executeaction.gql b/client/graph/admin/storage/storage-mutation-executeaction.gql
deleted file mode 100644
index aa0fb07c..00000000
--- a/client/graph/admin/storage/storage-mutation-executeaction.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation($targetKey: String!, $handler: String!) {
- storage {
- executeAction(targetKey: $targetKey, handler: $handler) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/storage/storage-mutation-save-targets.gql b/client/graph/admin/storage/storage-mutation-save-targets.gql
deleted file mode 100644
index 153ec199..00000000
--- a/client/graph/admin/storage/storage-mutation-save-targets.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation($targets: [StorageTargetInput]!) {
- storage {
- updateTargets(targets: $targets) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/storage/storage-query-status.gql b/client/graph/admin/storage/storage-query-status.gql
deleted file mode 100644
index 9a9e1f44..00000000
--- a/client/graph/admin/storage/storage-query-status.gql
+++ /dev/null
@@ -1,11 +0,0 @@
-query {
- storage {
- status {
- key
- title
- status
- message
- lastAttempt
- }
- }
-}
diff --git a/client/graph/admin/storage/storage-query-targets.gql b/client/graph/admin/storage/storage-query-targets.gql
deleted file mode 100644
index 71e6e619..00000000
--- a/client/graph/admin/storage/storage-query-targets.gql
+++ /dev/null
@@ -1,27 +0,0 @@
-query {
- storage {
- targets {
- isAvailable
- isEnabled
- key
- title
- description
- logo
- website
- supportedModes
- mode
- hasSchedule
- syncInterval
- syncIntervalDefault
- config {
- key
- value
- }
- actions {
- handler
- label
- hint
- }
- }
- }
-}
diff --git a/client/graph/admin/system/system-mutation-upgrade.gql b/client/graph/admin/system/system-mutation-upgrade.gql
deleted file mode 100644
index 030a4449..00000000
--- a/client/graph/admin/system/system-mutation-upgrade.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation {
- system {
- performUpgrade {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/system/system-query-info.gql b/client/graph/admin/system/system-query-info.gql
deleted file mode 100644
index e14cf680..00000000
--- a/client/graph/admin/system/system-query-info.gql
+++ /dev/null
@@ -1,21 +0,0 @@
-query {
- system {
- info {
- configFile
- cpuCores
- currentVersion
- dbHost
- dbType
- dbVersion
- hostname
- latestVersion
- latestVersionReleaseDate
- nodeVersion
- operatingSystem
- platform
- ramTotal
- upgradeCapable
- workingDirectory
- }
- }
-}
diff --git a/client/graph/admin/theme/theme-mutation-save.gql b/client/graph/admin/theme/theme-mutation-save.gql
deleted file mode 100644
index 856442ce..00000000
--- a/client/graph/admin/theme/theme-mutation-save.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation($theme: String!, $iconset: String!, $darkMode: Boolean!, $injectCSS: String, $injectHead: String, $injectBody: String) {
- theming {
- setConfig(theme: $theme, iconset: $iconset, darkMode: $darkMode, injectCSS: $injectCSS, injectHead: $injectHead, injectBody: $injectBody) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/theme/theme-query-config.gql b/client/graph/admin/theme/theme-query-config.gql
deleted file mode 100644
index 360cb2fa..00000000
--- a/client/graph/admin/theme/theme-query-config.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-query {
- theming {
- config {
- theme
- iconset
- darkMode
- injectCSS
- injectHead
- injectBody
- }
- }
-}
diff --git a/client/graph/admin/users/users-mutation-create.gql b/client/graph/admin/users/users-mutation-create.gql
deleted file mode 100644
index 90861007..00000000
--- a/client/graph/admin/users/users-mutation-create.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation ($providerKey: String!, $email: String!, $name: String!, $passwordRaw: String, $groups: [Int]!, $mustChangePassword: Boolean, $sendWelcomeEmail: Boolean) {
- users {
- create(providerKey: $providerKey, email: $email, name: $name, passwordRaw: $passwordRaw, groups: $groups, mustChangePassword: $mustChangePassword, sendWelcomeEmail: $sendWelcomeEmail) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/users/users-query-groups.gql b/client/graph/admin/users/users-query-groups.gql
deleted file mode 100644
index 4bc1b91f..00000000
--- a/client/graph/admin/users/users-query-groups.gql
+++ /dev/null
@@ -1,9 +0,0 @@
-query {
- groups {
- list {
- id
- name
- isSystem
- }
- }
-}
diff --git a/client/graph/admin/utilities/utilities-mutation-auth-regencerts.gql b/client/graph/admin/utilities/utilities-mutation-auth-regencerts.gql
deleted file mode 100644
index d45e971a..00000000
--- a/client/graph/admin/utilities/utilities-mutation-auth-regencerts.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation {
- authentication {
- regenerateCertificates {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/utilities/utilities-mutation-auth-resetguest.gql b/client/graph/admin/utilities/utilities-mutation-auth-resetguest.gql
deleted file mode 100644
index 078fe26f..00000000
--- a/client/graph/admin/utilities/utilities-mutation-auth-resetguest.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation {
- authentication {
- resetGuestUser {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/utilities/utilities-mutation-cache-flushcache.gql b/client/graph/admin/utilities/utilities-mutation-cache-flushcache.gql
deleted file mode 100644
index 92a5bd2e..00000000
--- a/client/graph/admin/utilities/utilities-mutation-cache-flushcache.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation {
- pages {
- flushCache {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/utilities/utilities-mutation-cache-flushuploads.gql b/client/graph/admin/utilities/utilities-mutation-cache-flushuploads.gql
deleted file mode 100644
index c72d0605..00000000
--- a/client/graph/admin/utilities/utilities-mutation-cache-flushuploads.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation {
- assets {
- flushTempUploads {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/utilities/utilities-mutation-content-migratelocale.gql b/client/graph/admin/utilities/utilities-mutation-content-migratelocale.gql
deleted file mode 100644
index 3b759361..00000000
--- a/client/graph/admin/utilities/utilities-mutation-content-migratelocale.gql
+++ /dev/null
@@ -1,13 +0,0 @@
-mutation($sourceLocale: String!, $targetLocale: String!) {
- pages {
- migrateToLocale(sourceLocale: $sourceLocale, targetLocale: $targetLocale) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- count
- }
- }
-}
diff --git a/client/graph/admin/utilities/utilities-mutation-content-rebuildtree.gql b/client/graph/admin/utilities/utilities-mutation-content-rebuildtree.gql
deleted file mode 100644
index 4f307fde..00000000
--- a/client/graph/admin/utilities/utilities-mutation-content-rebuildtree.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation {
- pages {
- rebuildTree {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/utilities/utilities-mutation-importv1-users.gql b/client/graph/admin/utilities/utilities-mutation-importv1-users.gql
deleted file mode 100644
index 54319baf..00000000
--- a/client/graph/admin/utilities/utilities-mutation-importv1-users.gql
+++ /dev/null
@@ -1,19 +0,0 @@
-mutation($mongoDbConnString: String!, $groupMode: SystemImportUsersGroupMode!) {
- system {
- importUsersFromV1(mongoDbConnString: $mongoDbConnString, groupMode: $groupMode) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- usersCount
- groupsCount
- failed {
- provider
- email
- error
- }
- }
- }
-}
diff --git a/client/graph/admin/utilities/utilities-mutation-telemetry-resetid.gql b/client/graph/admin/utilities/utilities-mutation-telemetry-resetid.gql
deleted file mode 100644
index d0e20005..00000000
--- a/client/graph/admin/utilities/utilities-mutation-telemetry-resetid.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation {
- system {
- resetTelemetryClientId {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/utilities/utilities-mutation-telemetry-set.gql b/client/graph/admin/utilities/utilities-mutation-telemetry-set.gql
deleted file mode 100644
index c26a4e2d..00000000
--- a/client/graph/admin/utilities/utilities-mutation-telemetry-set.gql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation($enabled: Boolean!) {
- system {
- setTelemetry(enabled: $enabled) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
-}
diff --git a/client/graph/admin/utilities/utilities-query-telemetry.gql b/client/graph/admin/utilities/utilities-query-telemetry.gql
deleted file mode 100644
index cbfe83ce..00000000
--- a/client/graph/admin/utilities/utilities-query-telemetry.gql
+++ /dev/null
@@ -1,8 +0,0 @@
-query {
- system {
- info {
- telemetry
- telemetryClientId
- }
- }
-}
diff --git a/client/graph/login/login-mutation-changepassword.gql b/client/graph/login/login-mutation-changepassword.gql
deleted file mode 100644
index 127c5644..00000000
--- a/client/graph/login/login-mutation-changepassword.gql
+++ /dev/null
@@ -1,13 +0,0 @@
-mutation($continuationToken: String!, $newPassword: String!) {
- authentication {
- loginChangePassword(continuationToken: $continuationToken, newPassword: $newPassword) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- jwt
- }
- }
-}
diff --git a/client/graph/login/login-mutation-login.gql b/client/graph/login/login-mutation-login.gql
deleted file mode 100644
index 11e90a8c..00000000
--- a/client/graph/login/login-mutation-login.gql
+++ /dev/null
@@ -1,16 +0,0 @@
-mutation($username: String!, $password: String!, $strategy: String!) {
- authentication {
- login(username: $username, password: $password, strategy: $strategy) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- jwt
- mustChangePwd
- mustProvideTFA
- continuationToken
- }
- }
-}
diff --git a/client/graph/login/login-mutation-tfa.gql b/client/graph/login/login-mutation-tfa.gql
deleted file mode 100644
index 116362e5..00000000
--- a/client/graph/login/login-mutation-tfa.gql
+++ /dev/null
@@ -1,13 +0,0 @@
-mutation($continuationToken: String!, $securityCode: String!) {
- authentication {
- loginTFA(continuationToken: $continuationToken, securityCode: $securityCode) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- jwt
- }
- }
-}
diff --git a/client/graph/login/login-query-strategies.gql b/client/graph/login/login-query-strategies.gql
deleted file mode 100644
index 5f8914d2..00000000
--- a/client/graph/login/login-query-strategies.gql
+++ /dev/null
@@ -1,14 +0,0 @@
-query {
- authentication {
- strategies(
- isEnabled: true
- ) {
- key
- title
- useForm
- icon
- color
- selfRegistration
- }
- }
-}
diff --git a/client/graph/register/register-mutation-create.gql b/client/graph/register/register-mutation-create.gql
deleted file mode 100644
index 16cc9836..00000000
--- a/client/graph/register/register-mutation-create.gql
+++ /dev/null
@@ -1,13 +0,0 @@
-mutation($email: String!, $password: String!, $name: String!) {
- authentication {
- register(email: $email, password: $password, name: $name) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- jwt
- }
- }
-}
diff --git a/client/index-app.js b/client/index-app.js
index 51d4d5de..0fa44f7e 100644
--- a/client/index-app.js
+++ b/client/index-app.js
@@ -1,7 +1,6 @@
require('core-js/stable')
require('regenerator-runtime/runtime')
-/* global siteConfig */
/* eslint-disable no-unused-expressions */
switch (window.document.documentElement.lang) {
@@ -17,10 +16,10 @@ switch (window.document.documentElement.lang) {
require('modernizr')
require('./scss/app.scss')
-import(/* webpackChunkName: "theme" */ './themes/' + siteConfig.theme + '/scss/app.scss')
+import(/* webpackChunkName: "theme" */ './themes/default/scss/app.scss')
import(/* webpackChunkName: "mdi" */ '@mdi/font/css/materialdesignicons.css')
require('./helpers/compatibility.js')
require('./client-app.js')
-import(/* webpackChunkName: "theme" */ './themes/' + siteConfig.theme + '/js/app.js')
+import(/* webpackChunkName: "theme" */ './themes/default/js/app.js')
diff --git a/client/modules/localization.js b/client/modules/localization.js
index 2fa865ac..72fc8c64 100644
--- a/client/modules/localization.js
+++ b/client/modules/localization.js
@@ -28,25 +28,27 @@ export default {
loadPath: '{{lng}}/{{ns}}',
parse: (data) => data,
ajax: (url, opts, cb, data) => {
- let langParams = url.split('/')
- graphQL.query({
- query: localeQuery,
- variables: {
- locale: langParams[0],
- namespace: langParams[1]
- }
- }).then(resp => {
- let ns = {}
- if (_.get(resp, 'data.localization.translations', []).length > 0) {
- resp.data.localization.translations.forEach(entry => {
- _.set(ns, entry.key, entry.value)
- })
- }
- return cb(ns, {status: '200'})
- }).catch(err => {
- console.error(err)
- return cb(null, {status: '404'})
- })
+ let ns = {}
+ return cb(ns, {status: '200'})
+ // let langParams = url.split('/')
+ // graphQL.query({
+ // query: localeQuery,
+ // variables: {
+ // locale: langParams[0],
+ // namespace: langParams[1]
+ // }
+ // }).then(resp => {
+ // let ns = {}
+ // if (_.get(resp, 'data.localization.translations', []).length > 0) {
+ // resp.data.localization.translations.forEach(entry => {
+ // _.set(ns, entry.key, entry.value)
+ // })
+ // }
+ // return cb(ns, {status: '200'})
+ // }).catch(err => {
+ // console.error(err)
+ // return cb(null, {status: '404'})
+ // })
}
}
]
diff --git a/client/static/browserconfig.xml b/client/static/browserconfig.xml
index 86c3f303..eaba6662 100644
--- a/client/static/browserconfig.xml
+++ b/client/static/browserconfig.xml
@@ -2,7 +2,7 @@
-
+
#1976d2
diff --git a/client/static/manifest.json b/client/static/manifest.json
index 47fc6f33..b28a23a2 100644
--- a/client/static/manifest.json
+++ b/client/static/manifest.json
@@ -4,12 +4,12 @@
"start_url": "/",
"icons": [
{
- "src": "/_assets/favicons/android-chrome-192x192.png",
+ "src": "/_assets-legacy/favicons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
- "src": "/_assets/favicons/android-chrome-256x256.png",
+ "src": "/_assets-legacy/favicons/android-chrome-256x256.png",
"sizes": "256x256",
"type": "image/png"
}
diff --git a/client/store/admin.js b/client/store/admin.js
deleted file mode 100644
index cb9d236e..00000000
--- a/client/store/admin.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { make } from 'vuex-pathify'
-
-const state = {
- info: {
- currentVersion: 'n/a',
- latestVersion: 'n/a',
- groupsTotal: 0,
- pagesTotal: 0,
- usersTotal: 0
- }
-}
-
-export default {
- namespaced: true,
- state,
- mutations: make.mutations(state)
-}
diff --git a/client/store/site.js b/client/store/site.js
index e26830e3..9f8fff46 100644
--- a/client/store/site.js
+++ b/client/store/site.js
@@ -3,10 +3,10 @@ import { make } from 'vuex-pathify'
/* global siteConfig */
const state = {
+ id: siteConfig.id,
company: siteConfig.company,
contentLicense: siteConfig.contentLicense,
dark: siteConfig.darkMode,
- mascot: true,
title: siteConfig.title,
logoUrl: siteConfig.logoUrl,
search: '',
diff --git a/config.sample.yml b/config.sample.yml
index b98f38d1..f4ba837e 100644
--- a/config.sample.yml
+++ b/config.sample.yml
@@ -93,7 +93,7 @@ bindIP: 0.0.0.0
# ---------------------------------------------------------------------
# Logging
# ---------------------------------------------------------------------
-# Possible values: error, warn, info (default), verbose, debug, silly
+# Possible values: error, warn, info (default), debug
logLevel: info
diff --git a/dev/templates/base.pug b/dev/templates/base.pug
index fa90ce42..ff019ecf 100644
--- a/dev/templates/base.pug
+++ b/dev/templates/base.pug
@@ -6,7 +6,7 @@ html(lang=siteConfig.lang)
meta(name='viewport', content='user-scalable=yes, width=device-width, initial-scale=1, maximum-scale=5')
meta(name='theme-color', content='#1976d2')
meta(name='msapplication-TileColor', content='#1976d2')
- meta(name='msapplication-TileImage', content='/_assets/favicons/mstile-150x150.png')
+ meta(name='msapplication-TileImage', content='/_assets-legacy/favicons/mstile-150x150.png')
title= pageMeta.title + ' | ' + config.title
@@ -20,12 +20,12 @@ html(lang=siteConfig.lang)
meta(property='og:site_name', content=config.title)
//- Favicon
- link(rel='apple-touch-icon', sizes='180x180', href='/_assets/favicons/apple-touch-icon.png')
- link(rel='icon', type='image/png', sizes='192x192', href='/_assets/favicons/android-chrome-192x192.png')
- link(rel='icon', type='image/png', sizes='32x32', href='/_assets/favicons/favicon-32x32.png')
- link(rel='icon', type='image/png', sizes='16x16', href='/_assets/favicons/favicon-16x16.png')
- link(rel='mask-icon', href='/_assets/favicons/safari-pinned-tab.svg', color='#1976d2')
- link(rel='manifest', href='/_assets/manifest.json')
+ link(rel='apple-touch-icon', sizes='180x180', href='/_assets-legacy/favicons/apple-touch-icon.png')
+ link(rel='icon', type='image/png', sizes='192x192', href='/_assets-legacy/favicons/android-chrome-192x192.png')
+ link(rel='icon', type='image/png', sizes='32x32', href='/_assets-legacy/favicons/favicon-32x32.png')
+ link(rel='icon', type='image/png', sizes='16x16', href='/_assets-legacy/favicons/favicon-16x16.png')
+ link(rel='mask-icon', href='/_assets-legacy/favicons/safari-pinned-tab.svg', color='#1976d2')
+ link(rel='manifest', href='/_assets-legacy/manifest.json')
//- Site Properties
script.
diff --git a/package.json b/package.json
index 9c73d22e..e1725b2e 100644
--- a/package.json
+++ b/package.json
@@ -165,6 +165,7 @@
"semver": "7.3.7",
"serve-favicon": "2.5.0",
"simple-git": "2.21.0",
+ "socket.io": "4.5.2",
"ssh2": "1.9.0",
"ssh2-promise": "1.0.2",
"striptags": "3.2.0",
diff --git a/server/controllers/auth.js b/server/controllers/auth.js
index a63df757..03092eed 100644
--- a/server/controllers/auth.js
+++ b/server/controllers/auth.js
@@ -81,11 +81,13 @@ router.all('/login/:strategy/callback', async (req, res, next) => {
/**
* Logout
*/
-router.get('/logout', async (req, res) => {
+router.get('/logout', async (req, res, next) => {
const redirURL = await WIKI.models.users.logout({ req, res })
- req.logout()
- res.clearCookie('jwt')
- res.redirect(redirURL)
+ req.logout((err) => {
+ if (err) { return next(err) }
+ res.clearCookie('jwt')
+ res.redirect(redirURL)
+ })
})
/**
diff --git a/server/controllers/common.js b/server/controllers/common.js
index f0b985d0..d55ff351 100644
--- a/server/controllers/common.js
+++ b/server/controllers/common.js
@@ -113,11 +113,15 @@ router.get(['/d', '/d/*'], async (req, res, next) => {
/**
* Create/Edit document
*/
-router.get(['/e', '/e/*'], async (req, res, next) => {
+router.get(['/_edit', '/_edit/*'], async (req, res, next) => {
const pageArgs = pageHelper.parsePath(req.path, { stripExt: true })
+ if (pageArgs.path === '') {
+ return res.redirect(`/_edit/home`)
+ }
+
if (WIKI.config.lang.namespacing && !pageArgs.explicitLocale) {
- return res.redirect(`/e/${pageArgs.locale}/${pageArgs.path}`)
+ return res.redirect(`/_edit/${pageArgs.locale}/${pageArgs.path}`)
}
req.i18n.changeLanguage(pageArgs.locale)
diff --git a/server/controllers/ws.js b/server/controllers/ws.js
new file mode 100644
index 00000000..51151c93
--- /dev/null
+++ b/server/controllers/ws.js
@@ -0,0 +1,31 @@
+const chalk = require('chalk')
+const os = require('node:os')
+
+/* global WIKI */
+
+module.exports = () => {
+ WIKI.servers.ws.on('connection', (socket) => {
+ // TODO: Validate token + permissions
+ const token = socket.handshake.auth.token
+ console.info(token)
+
+ const listeners = {}
+
+ socket.on('server:logs', () => {
+ socket.emit('server:log', chalk`{greenBright Streaming logs from {bold Wiki.js} instance {yellowBright.bold ${WIKI.INSTANCE_ID}} on host {yellowBright.bold ${os.hostname()}}...}`)
+ listeners.serverLogs = (msg) => {
+ socket.emit('server:log', msg)
+ }
+ WIKI.logger.ws.addListener('log', listeners.serverLogs)
+ WIKI.logger.warn(`User XYZ is streaming server logs. ( Listeners: ${WIKI.logger.ws.listenerCount('log')} )`)
+ })
+
+ socket.on('disconnect', () => {
+ if (listeners.serverLogs) {
+ WIKI.logger.ws.removeListener('log', listeners.serverLogs)
+ delete listeners.serverLogs
+ }
+ WIKI.logger.warn(`User XYZ has stopped streaming server logs. ( Listeners: ${WIKI.logger.ws.listenerCount('log')} )`)
+ })
+ })
+}
diff --git a/server/core/auth.js b/server/core/auth.js
index 09eca7e7..848fb7a6 100644
--- a/server/core/auth.js
+++ b/server/core/auth.js
@@ -112,6 +112,7 @@ module.exports = {
WIKI.auth.passport.authenticate('jwt', {session: false}, async (err, user, info) => {
if (err) { return next() }
let mustRevalidate = false
+ const strategyId = user.pvd
// Expired but still valid within N days, just renew
if (info instanceof Error && info.name === 'TokenExpiredError') {
@@ -143,10 +144,11 @@ module.exports = {
if (mustRevalidate) {
const jwtPayload = jwt.decode(securityHelper.extractJWT(req))
try {
- const newToken = await WIKI.models.users.refreshToken(jwtPayload.id)
+ const newToken = await WIKI.models.users.refreshToken(jwtPayload.id, jwtPayload.pvd)
user = newToken.user
user.permissions = user.getPermissions()
user.groups = user.getGroups()
+ user.strategyId = strategyId
req.user = user
// Try headers, otherwise cookies for response
@@ -163,6 +165,7 @@ module.exports = {
user = await WIKI.models.users.getById(user.id)
user.permissions = user.getPermissions()
user.groups = user.getGroups()
+ user.strategyId = strategyId
req.user = user
} else {
// JWT is NOT valid, set as guest
diff --git a/server/core/logger.js b/server/core/logger.js
index 912f2171..664296fa 100644
--- a/server/core/logger.js
+++ b/server/core/logger.js
@@ -17,6 +17,8 @@ const primaryLogger = new Logger()
let ignoreNextLevels = false
+primaryLogger.ws = new EventEmitter()
+
LEVELS.forEach(lvl => {
primaryLogger[lvl] = (...args) => {
primaryLogger.emit(lvl, ...args)
@@ -24,16 +26,20 @@ LEVELS.forEach(lvl => {
if (!ignoreNextLevels) {
primaryLogger.on(lvl, (msg) => {
+ let formatted = ''
if (WIKI.config.logFormat === 'json') {
- console.log(JSON.stringify({
+ formatted = JSON.stringify({
timestamp: new Date().toISOString(),
instance: WIKI.INSTANCE_ID,
level: lvl,
message: msg
- }))
+ })
} else {
- console.log(chalk`${new Date().toISOString()} {dim [${WIKI.INSTANCE_ID}]} {${LEVELCOLORS[lvl]}.bold ${lvl}}: ${msg}`)
+ formatted = chalk`${new Date().toISOString()} {dim [${WIKI.INSTANCE_ID}]} {${LEVELCOLORS[lvl]}.bold ${lvl}}: ${msg}`
}
+
+ console.log(formatted)
+ primaryLogger.ws.emit('log', formatted)
})
}
if (lvl === WIKI.config.logLevel) {
diff --git a/server/core/servers.js b/server/core/servers.js
index 2bc30ffa..5ba59982 100644
--- a/server/core/servers.js
+++ b/server/core/servers.js
@@ -4,27 +4,26 @@ const https = require('https')
const { ApolloServer } = require('apollo-server-express')
const Promise = require('bluebird')
const _ = require('lodash')
+const io = require('socket.io')
const { ApolloServerPluginLandingPageGraphQLPlayground, ApolloServerPluginLandingPageProductionDefault } = require('apollo-server-core')
/* global WIKI */
module.exports = {
- servers: {
- graph: null,
- http: null,
- https: null
- },
+ graph: null,
+ http: null,
+ https: null,
+ ws: null,
connections: new Map(),
le: null,
/**
- * Start HTTP Server
+ * Initialize HTTP Server
*/
- async startHTTP () {
+ async initHTTP () {
WIKI.logger.info(`HTTP Server on port: [ ${WIKI.config.port} ]`)
- this.servers.http = http.createServer(WIKI.app)
+ this.http = http.createServer(WIKI.app)
- this.servers.http.listen(WIKI.config.port, WIKI.config.bindIP)
- this.servers.http.on('error', (error) => {
+ this.http.on('error', (error) => {
if (error.syscall !== 'listen') {
throw error
}
@@ -41,11 +40,11 @@ module.exports = {
}
})
- this.servers.http.on('listening', () => {
+ this.http.on('listening', () => {
WIKI.logger.info('HTTP Server: [ RUNNING ]')
})
- this.servers.http.on('connection', conn => {
+ this.http.on('connection', conn => {
let connKey = `http:${conn.remoteAddress}:${conn.remotePort}`
this.connections.set(connKey, conn)
conn.on('close', () => {
@@ -54,9 +53,15 @@ module.exports = {
})
},
/**
- * Start HTTPS Server
+ * Start HTTP Server
*/
- async startHTTPS () {
+ async startHTTP () {
+ this.http.listen(WIKI.config.port, WIKI.config.bindIP)
+ },
+ /**
+ * Initialize HTTPS Server
+ */
+ async initHTTPS () {
if (WIKI.config.ssl.provider === 'letsencrypt') {
this.le = require('./letsencrypt')
await this.le.init()
@@ -82,10 +87,10 @@ module.exports = {
WIKI.logger.error(err)
return process.exit(1)
}
- this.servers.https = https.createServer(tlsOpts, WIKI.app)
+ this.https = https.createServer(tlsOpts, WIKI.app)
- this.servers.https.listen(WIKI.config.ssl.port, WIKI.config.bindIP)
- this.servers.https.on('error', (error) => {
+ this.https.listen(WIKI.config.ssl.port, WIKI.config.bindIP)
+ this.https.on('error', (error) => {
if (error.syscall !== 'listen') {
throw error
}
@@ -102,11 +107,11 @@ module.exports = {
}
})
- this.servers.https.on('listening', () => {
+ this.https.on('listening', () => {
WIKI.logger.info('HTTPS Server: [ RUNNING ]')
})
- this.servers.https.on('connection', conn => {
+ this.https.on('connection', conn => {
let connKey = `https:${conn.remoteAddress}:${conn.remotePort}`
this.connections.set(connKey, conn)
conn.on('close', () => {
@@ -114,12 +119,18 @@ module.exports = {
})
})
},
+ /**
+ * Start HTTPS Server
+ */
+ async startHTTPS () {
+ this.https.listen(WIKI.config.ssl.port, WIKI.config.bindIP)
+ },
/**
* Start GraphQL Server
*/
async startGraphQL () {
const graphqlSchema = require('../graph')
- this.servers.graph = new ApolloServer({
+ this.graph = new ApolloServer({
schema: graphqlSchema,
uploads: false,
context: ({ req, res }) => ({ req, res }),
@@ -129,12 +140,31 @@ module.exports = {
}) : ApolloServerPluginLandingPageProductionDefault({
footer: false
})
- // ApolloServerPluginDrainHttpServer({ httpServer: this.servers.http })
- // ...(this.servers.https && ApolloServerPluginDrainHttpServer({ httpServer: this.servers.https }))
+ // ApolloServerPluginDrainHttpServer({ httpServer: this.http })
+ // ...(this.https && ApolloServerPluginDrainHttpServer({ httpServer: this.https }))
]
})
- await this.servers.graph.start()
- this.servers.graph.applyMiddleware({ app: WIKI.app, cors: false, path: '/_graphql' })
+ await this.graph.start()
+ this.graph.applyMiddleware({ app: WIKI.app, cors: false, path: '/_graphql' })
+ },
+ /**
+ * Start Socket.io WebSocket Server
+ */
+ async initWebSocket() {
+ if (this.https) {
+ this.ws = new io.Server(this.https, {
+ path: '/_ws/',
+ serveClient: false
+ })
+ WIKI.logger.info(`WebSocket Server attached to HTTPS Server [ OK ]`)
+ } else {
+ this.ws = new io.Server(this.http, {
+ path: '/_ws/',
+ serveClient: false,
+ cors: true // TODO: dev only, replace with app settings once stable
+ })
+ WIKI.logger.info(`WebSocket Server attached to HTTP Server [ OK ]`)
+ }
},
/**
* Close all active connections
@@ -156,15 +186,15 @@ module.exports = {
*/
async stopServers () {
this.closeConnections()
- if (this.servers.http) {
- await Promise.fromCallback(cb => { this.servers.http.close(cb) })
- this.servers.http = null
+ if (this.http) {
+ await Promise.fromCallback(cb => { this.http.close(cb) })
+ this.http = null
}
- if (this.servers.https) {
- await Promise.fromCallback(cb => { this.servers.https.close(cb) })
- this.servers.https = null
+ if (this.https) {
+ await Promise.fromCallback(cb => { this.https.close(cb) })
+ this.https = null
}
- this.servers.graph = null
+ this.graph = null
},
/**
* Restart Server
@@ -173,17 +203,19 @@ module.exports = {
this.closeConnections(srv)
switch (srv) {
case 'http':
- if (this.servers.http) {
- await Promise.fromCallback(cb => { this.servers.http.close(cb) })
- this.servers.http = null
+ if (this.http) {
+ await Promise.fromCallback(cb => { this.http.close(cb) })
+ this.http = null
}
+ this.initHTTP()
this.startHTTP()
break
case 'https':
- if (this.servers.https) {
- await Promise.fromCallback(cb => { this.servers.https.close(cb) })
- this.servers.https = null
+ if (this.https) {
+ await Promise.fromCallback(cb => { this.https.close(cb) })
+ this.https = null
}
+ this.initHTTPS()
this.startHTTPS()
break
default:
diff --git a/server/graph/resolvers/hooks.js b/server/graph/resolvers/hooks.js
index 9c5e8e7a..cf30dd6a 100644
--- a/server/graph/resolvers/hooks.js
+++ b/server/graph/resolvers/hooks.js
@@ -6,6 +6,7 @@ const _ = require('lodash')
module.exports = {
Query: {
async hooks () {
+ WIKI.logger.warn('Seriously man')
return WIKI.models.hooks.query().orderBy('name')
},
async hookById (obj, args) {
@@ -20,16 +21,18 @@ module.exports = {
try {
// -> Validate inputs
if (!args.name || args.name.length < 1) {
- throw WIKI.ERROR(new Error('Invalid Hook Name'), 'HookCreateInvalidName')
+ throw new WIKI.Error.Custom('HookCreateInvalidName', 'Invalid Hook Name')
}
if (!args.events || args.events.length < 1) {
- throw WIKI.ERROR(new Error('Invalid Hook Events'), 'HookCreateInvalidEvents')
+ throw new WIKI.Error.Custom('HookCreateInvalidEvents', 'Invalid Hook Events')
}
if (!args.url || args.url.length < 8 || !args.url.startsWith('http')) {
- throw WIKI.ERROR(new Error('Invalid Hook URL'), 'HookCreateInvalidURL')
+ throw new WIKI.Error.Custom('HookCreateInvalidURL', 'Invalid Hook URL')
}
// -> Create hook
const newHook = await WIKI.models.hooks.createHook(args)
+ WIKI.logger.debug(`New Hook ${newHook.id} created successfully.`)
+
return {
operation: graphHelper.generateSuccess('Hook created successfully'),
hook: newHook
@@ -46,20 +49,21 @@ module.exports = {
// -> Load hook
const hook = await WIKI.models.hooks.query().findById(args.id)
if (!hook) {
- throw WIKI.ERROR(new Error('Invalid Hook ID'), 'HookInvalidId')
+ throw new WIKI.Error.Custom('HookInvalidId', 'Invalid Hook ID')
}
// -> Check for bad input
if (_.has(args.patch, 'name') && args.patch.name.length < 1) {
- throw WIKI.ERROR(new Error('Invalid Hook Name'), 'HookCreateInvalidName')
+ throw new WIKI.Error.Custom('HookCreateInvalidName', 'Invalid Hook Name')
}
if (_.has(args.patch, 'events') && args.patch.events.length < 1) {
- throw WIKI.ERROR(new Error('Invalid Hook Events'), 'HookCreateInvalidEvents')
+ throw new WIKI.Error.Custom('HookCreateInvalidEvents', 'Invalid Hook Events')
}
if (_.has(args.patch, 'url') && (_.trim(args.patch.url).length < 8 || !args.patch.url.startsWith('http'))) {
- throw WIKI.ERROR(new Error('URL is invalid.'), 'HookInvalidURL')
+ throw new WIKI.Error.Custom('HookInvalidURL', 'URL is invalid.')
}
// -> Update hook
await WIKI.models.hooks.query().findById(args.id).patch(args.patch)
+ WIKI.logger.debug(`Hook ${args.id} updated successfully.`)
return {
operation: graphHelper.generateSuccess('Hook updated successfully')
@@ -74,6 +78,7 @@ module.exports = {
async deleteHook (obj, args) {
try {
await WIKI.models.hooks.deleteHook(args.id)
+ WIKI.logger.debug(`Hook ${args.id} deleted successfully.`)
return {
operation: graphHelper.generateSuccess('Hook deleted successfully')
}
diff --git a/server/graph/resolvers/mail.js b/server/graph/resolvers/mail.js
index a86c443b..611c2376 100644
--- a/server/graph/resolvers/mail.js
+++ b/server/graph/resolvers/mail.js
@@ -30,7 +30,7 @@ module.exports = {
})
return {
- responseResult: graphHelper.generateSuccess('Test email sent successfully.')
+ operation: graphHelper.generateSuccess('Test email sent successfully.')
}
} catch (err) {
return graphHelper.generateError(err)
@@ -58,7 +58,7 @@ module.exports = {
WIKI.mail.init()
return {
- responseResult: graphHelper.generateSuccess('Mail configuration updated successfully.')
+ operation: graphHelper.generateSuccess('Mail configuration updated successfully.')
}
} catch (err) {
return graphHelper.generateError(err)
diff --git a/server/graph/resolvers/site.js b/server/graph/resolvers/site.js
index 5fec0acf..9dd1b24e 100644
--- a/server/graph/resolvers/site.js
+++ b/server/graph/resolvers/site.js
@@ -53,10 +53,10 @@ module.exports = {
try {
// -> Validate inputs
if (!args.hostname || args.hostname.length < 1 || !/^(\\*)|([a-z0-9\-.:]+)$/.test(args.hostname)) {
- throw WIKI.ERROR(new Error('Invalid Site Hostname'), 'SiteCreateInvalidHostname')
+ throw new WIKI.Error.Custom('SiteCreateInvalidHostname', 'Invalid Site Hostname')
}
if (!args.title || args.title.length < 1 || !/^[^<>"]+$/.test(args.title)) {
- throw WIKI.ERROR(new Error('Invalid Site Title'), 'SiteCreateInvalidTitle')
+ throw new WIKI.Error.Custom('SiteCreateInvalidTitle', 'Invalid Site Title')
}
// -> Check for duplicate catch-all
if (args.hostname === '*') {
@@ -64,7 +64,7 @@ module.exports = {
hostname: args.hostname
}).first()
if (site) {
- throw WIKI.ERROR(new Error('A site with a catch-all hostname already exists! Cannot have 2 catch-all hostnames.'), 'SiteCreateDuplicateCatchAll')
+ throw new WIKI.Error.Custom('SiteCreateDuplicateCatchAll', 'A site with a catch-all hostname already exists! Cannot have 2 catch-all hostnames.')
}
}
// -> Create site
@@ -88,17 +88,17 @@ module.exports = {
// -> Load site
const site = await WIKI.models.sites.query().findById(args.id)
if (!site) {
- throw WIKI.ERROR(new Error('Invalid Site ID'), 'SiteInvalidId')
+ throw new WIKI.Error.Custom('SiteInvalidId', 'Invalid Site ID')
}
// -> Check for bad input
if (_.has(args.patch, 'hostname') && _.trim(args.patch.hostname).length < 1) {
- throw WIKI.ERROR(new Error('Hostname is invalid.'), 'SiteInvalidHostname')
+ throw new WIKI.Error.Custom('SiteInvalidHostname', 'Hostname is invalid.')
}
// -> Check for duplicate catch-all
if (args.patch.hostname === '*' && site.hostname !== '*') {
const dupSite = await WIKI.models.sites.query().where({ hostname: '*' }).first()
if (dupSite) {
- throw WIKI.ERROR(new Error(`Site ${dupSite.config.title} with a catch-all hostname already exists! Cannot have 2 catch-all hostnames.`), 'SiteUpdateDuplicateCatchAll')
+ throw new WIKI.Error.Custom('SiteUpdateDuplicateCatchAll', `Site ${dupSite.config.title} with a catch-all hostname already exists! Cannot have 2 catch-all hostnames.`)
}
}
// -> Format Code
@@ -132,7 +132,7 @@ module.exports = {
// -> Ensure site isn't last one
const sitesCount = await WIKI.models.sites.query().count('id').first()
if (sitesCount?.count && _.toNumber(sitesCount?.count) <= 1) {
- throw WIKI.ERROR(new Error('Cannot delete the last site. At least 1 site must exists at all times.'), 'SiteDeleteLastSite')
+ throw new WIKI.Error.Custom('SiteDeleteLastSite', 'Cannot delete the last site. At least 1 site must exists at all times.')
}
// -> Delete site
await WIKI.models.sites.deleteSite(args.id)
diff --git a/server/graph/resolvers/system.js b/server/graph/resolvers/system.js
index c73b737d..50ac3f11 100644
--- a/server/graph/resolvers/system.js
+++ b/server/graph/resolvers/system.js
@@ -30,6 +30,24 @@ module.exports = {
}
},
Mutation: {
+ async disconnectWS (obj, args, context) {
+ WIKI.servers.ws.disconnectSockets(true)
+ WIKI.logger.info('All active websocket connections have been terminated.')
+ return {
+ operation: graphHelper.generateSuccess('All websocket connections closed successfully.')
+ }
+ },
+ async installExtension (obj, args, context) {
+ try {
+ await WIKI.extensions.ext[args.key].install()
+ // TODO: broadcast ext install
+ return {
+ status: graphHelper.generateSuccess('Extension installed successfully')
+ }
+ } catch (err) {
+ return graphHelper.generateError(err)
+ }
+ },
async updateSystemFlags (obj, args, context) {
WIKI.config.flags = _.transform(args.flags, (result, row) => {
_.set(result, row.key, row.value)
@@ -47,17 +65,6 @@ module.exports = {
return {
status: graphHelper.generateSuccess('System Security configuration applied successfully')
}
- },
- async installExtension (obj, args, context) {
- try {
- await WIKI.extensions.ext[args.key].install()
- // TODO: broadcast ext install
- return {
- status: graphHelper.generateSuccess('Extension installed successfully')
- }
- } catch (err) {
- return graphHelper.generateError(err)
- }
}
},
SystemInfo: {
diff --git a/server/graph/scalars/date.js b/server/graph/scalars/date.js
index 4a20a6ff..f2939711 100644
--- a/server/graph/scalars/date.js
+++ b/server/graph/scalars/date.js
@@ -1,10 +1,19 @@
const gql = require('graphql')
+const { DateTime } = require('luxon')
+
+function parseDateTime (value) {
+ const nDate = DateTime.fromISO(value)
+ return nDate.isValid ? nDate : null
+}
module.exports = new gql.GraphQLScalarType({
name: 'Date',
description: 'ISO date-time string at UTC',
parseValue(value) {
- return new Date(value)
+ if (typeof value !== 'string') {
+ throw new TypeError('Date value must be an string!')
+ }
+ return parseDateTime(value)
},
serialize(value) {
return value.toISOString()
@@ -13,6 +22,6 @@ module.exports = new gql.GraphQLScalarType({
if (ast.kind !== gql.Kind.STRING) {
throw new TypeError('Date value must be an string!')
}
- return new Date(ast.value)
+ return parseDateTime(ast.value)
}
})
diff --git a/server/graph/schemas/page.graphql b/server/graph/schemas/page.graphql
index c87106c4..5c48ee83 100644
--- a/server/graph/schemas/page.graphql
+++ b/server/graph/schemas/page.graphql
@@ -64,11 +64,11 @@ extend type Query {
extend type Mutation {
createPage(
+ siteId: UUID!
content: String!
description: String!
editor: String!
isPublished: Boolean!
- isPrivate: Boolean!
locale: String!
path: String!
publishEndDate: Date
@@ -84,7 +84,6 @@ extend type Mutation {
content: String
description: String
editor: String
- isPrivate: Boolean
isPublished: Boolean
locale: String
path: String
@@ -164,9 +163,7 @@ type Page {
hash: String
title: String
description: String
- isPrivate: Boolean
isPublished: Boolean
- privateNS: String
publishStartDate: Date
publishEndDate: Date
tags: [PageTag]
diff --git a/server/graph/schemas/system.graphql b/server/graph/schemas/system.graphql
index 5fcf229f..f7301ad7 100644
--- a/server/graph/schemas/system.graphql
+++ b/server/graph/schemas/system.graphql
@@ -10,6 +10,12 @@ extend type Query {
}
extend type Mutation {
+ disconnectWS: DefaultResponse
+
+ installExtension(
+ key: String!
+ ): DefaultResponse
+
updateSystemFlags(
flags: [SystemFlagInput]!
): DefaultResponse
@@ -34,10 +40,6 @@ extend type Mutation {
uploadMaxFileSize: Int
uploadScanSVG: Boolean
): DefaultResponse
-
- installExtension(
- key: String!
- ): DefaultResponse
}
# -----------------------------------------------
diff --git a/server/helpers/error.js b/server/helpers/error.js
index 6bff807a..72a3b326 100644
--- a/server/helpers/error.js
+++ b/server/helpers/error.js
@@ -1,6 +1,9 @@
const CustomError = require('custom-error-instance')
module.exports = {
+ Custom (slug, message) {
+ return CustomError(slug, { message })
+ },
AssetDeleteForbidden: CustomError('AssetDeleteForbidden', {
message: 'You are not authorized to delete this asset.'
}),
diff --git a/server/helpers/page.js b/server/helpers/page.js
index 8049edc1..57714daf 100644
--- a/server/helpers/page.js
+++ b/server/helpers/page.js
@@ -42,7 +42,7 @@ module.exports = {
p = _.trim(p)
return !_.isEmpty(p) && p !== '..' && p !== '.'
})
- if (pathParts[0].length === 1) {
+ if (pathParts[0].startsWith('_')) {
pathParts.shift()
}
if (localeSegmentRegex.test(pathParts[0])) {
@@ -102,7 +102,7 @@ module.exports = {
*/
isReservedPath(rawPath) {
const firstSection = _.head(rawPath.split('/'))
- if (firstSection.length <= 1) {
+ if (firstSection.length < 1) {
return true
} else if (localeSegmentRegex.test(firstSection)) {
return true
diff --git a/server/models/pages.js b/server/models/pages.js
index 897927b0..b7d839a7 100644
--- a/server/models/pages.js
+++ b/server/models/pages.js
@@ -227,6 +227,11 @@ module.exports = class Page extends Model {
* @returns {Promise} Promise of the Page Model Instance
*/
static async createPage(opts) {
+ // -> Validate site
+ if (!WIKI.sites[opts.siteId]) {
+ throw new WIKI.Error.Custom('InvalidSiteId', 'Site ID is invalid.')
+ }
+
// -> Validate path
if (opts.path.includes('.') || opts.path.includes(' ') || opts.path.includes('\\') || opts.path.includes('//')) {
throw new WIKI.Error.PageIllegalPath()
@@ -295,8 +300,9 @@ module.exports = class Page extends Model {
publishState: opts.publishState,
localeCode: opts.locale,
path: opts.path,
- publishEndDate: opts.publishEndDate || '',
- publishStartDate: opts.publishStartDate || '',
+ publishEndDate: opts.publishEndDate?.toISO(),
+ publishStartDate: opts.publishStartDate?.toISO(),
+ siteId: opts.siteId,
title: opts.title,
toc: '[]',
extra: JSON.stringify({
diff --git a/server/models/users.js b/server/models/users.js
index f6cf793d..706aaefb 100644
--- a/server/models/users.js
+++ b/server/models/users.js
@@ -381,7 +381,7 @@ module.exports = class User extends Model {
return new Promise((resolve, reject) => {
context.req.login(user, { session: false }, async errc => {
if (errc) { return reject(errc) }
- const jwtToken = await WIKI.models.users.refreshToken(user)
+ const jwtToken = await WIKI.models.users.refreshToken(user, strategyId)
resolve({ jwt: jwtToken.token, redirect })
})
})
@@ -390,7 +390,7 @@ module.exports = class User extends Model {
/**
* Generate a new token for a user
*/
- static async refreshToken(user) {
+ static async refreshToken(user, provider) {
if (_.isString(user)) {
user = await WIKI.models.users.query().findById(user).withGraphFetched('groups').modifyGraph('groups', builder => {
builder.select('groups.id', 'permissions')
@@ -415,7 +415,8 @@ module.exports = class User extends Model {
token: jwt.sign({
id: user.id,
email: user.email,
- groups: user.getGroups()
+ groups: user.getGroups(),
+ ...provider && { pvd: provider }
}, {
key: WIKI.config.auth.certs.private,
passphrase: WIKI.config.auth.secret
@@ -831,9 +832,17 @@ module.exports = class User extends Model {
if (!context.req.user || context.req.user.id === WIKI.config.auth.guestUserId) {
return '/'
}
- const usr = await WIKI.models.users.query().findById(context.req.user.id).select('providerKey')
- const provider = _.find(WIKI.auth.strategies, ['key', usr.providerKey])
- return provider.logout ? provider.logout(provider.config) : '/'
+ if (context.req.user.strategyId && _.has(WIKI.auth.strategies, context.req.user.strategyId)) {
+ const selStrategy = WIKI.auth.strategies[context.req.user.strategyId]
+ if (!selStrategy.isEnabled) {
+ throw new WIKI.Error.AuthProviderInvalid()
+ }
+ const provider = _.find(WIKI.data.authentication, ['key', selStrategy.module])
+ if (provider.logout) {
+ return provider.logout(provider.config)
+ }
+ }
+ return '/'
}
static async getGuestUser () {
diff --git a/server/web.js b/server/web.js
index ba88ef9d..a2b61ba0 100644
--- a/server/web.js
+++ b/server/web.js
@@ -37,6 +37,26 @@ module.exports = async () => {
WIKI.app = app
app.use(compression())
+ // ----------------------------------------
+ // Initialize HTTP/HTTPS Server
+ // ----------------------------------------
+
+ const useHTTPS = WIKI.config.ssl.enabled === true || WIKI.config.ssl.enabled === 'true' || WIKI.config.ssl.enabled === 1 || WIKI.config.ssl.enabled === '1'
+
+ await WIKI.servers.initHTTP()
+
+ if (useHTTPS) {
+ await WIKI.servers.initHTTPS()
+ }
+
+ await WIKI.servers.initWebSocket()
+
+ // ----------------------------------------
+ // Attach WebSocket Server
+ // ----------------------------------------
+
+ ctrl.ws()
+
// ----------------------------------------
// Security
// ----------------------------------------
@@ -208,7 +228,7 @@ module.exports = async () => {
await WIKI.servers.startHTTP()
- if (WIKI.config.ssl.enabled === true || WIKI.config.ssl.enabled === 'true' || WIKI.config.ssl.enabled === 1 || WIKI.config.ssl.enabled === '1') {
+ if (useHTTPS) {
await WIKI.servers.startHTTPS()
}
diff --git a/ux/package.json b/ux/package.json
index cf2c4c2d..181f4215 100644
--- a/ux/package.json
+++ b/ux/package.json
@@ -72,6 +72,7 @@
"pinia": "2.0.20",
"pug": "3.0.2",
"quasar": "2.7.7",
+ "socket.io-client": "4.5.2",
"tippy.js": "6.3.7",
"uuid": "8.3.2",
"v-network-graph": "0.6.6",
@@ -81,6 +82,7 @@
"vue-router": "4.1.3",
"vue3-otp-input": "0.3.6",
"vuedraggable": "4.1.0",
+ "xterm": "4.19.0",
"zxcvbn": "4.4.2"
},
"devDependencies": {
diff --git a/ux/public/_assets/icons/fluent-linux-terminal-animated.svg b/ux/public/_assets/icons/fluent-linux-terminal-animated.svg
new file mode 100644
index 00000000..770515ee
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-linux-terminal-animated.svg
@@ -0,0 +1,26 @@
+
+
diff --git a/ux/public/_assets/icons/fluent-linux-terminal.svg b/ux/public/_assets/icons/fluent-linux-terminal.svg
new file mode 100644
index 00000000..fc1a6a65
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-linux-terminal.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-disconnected.svg b/ux/public/_assets/icons/ultraviolet-disconnected.svg
new file mode 100644
index 00000000..47ed81fc
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-disconnected.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/src/i18n/locales/en.json b/ux/src/i18n/locales/en.json
index f3a02bdd..76991e51 100644
--- a/ux/src/i18n/locales/en.json
+++ b/ux/src/i18n/locales/en.json
@@ -1482,5 +1482,19 @@
"welcome.title": "Welcome to Wiki.js!",
"welcome.subtitle": "Let's get started...",
"welcome.createHome": "Create the homepage",
- "welcome.admin": "Administration Area"
+ "welcome.admin": "Administration Area",
+ "admin.terminal.title": "Terminal",
+ "admin.terminal.subtitle": "View process logs in real-time",
+ "admin.terminal.command": "Command",
+ "admin.terminal.logs": "Logs",
+ "admin.terminal.connect": "Connect",
+ "admin.terminal.disconnect": "Disconnect",
+ "admin.terminal.clear": "Clear",
+ "admin.terminal.connecting": "Connecting to server...",
+ "admin.terminal.connected": "Connected.",
+ "admin.terminal.disconnected": "Disconnected.",
+ "admin.terminal.connectError": "Connection Error:",
+ "admin.utilities.disconnectWS": "Disconnect WebSocket Sessions",
+ "admin.utilities.disconnectWSHint": "Force all active websocket connections to be closed.",
+ "admin.utilities.disconnectWSSuccess": "All active websocket connections have been terminated."
}
diff --git a/ux/src/layouts/AdminLayout.vue b/ux/src/layouts/AdminLayout.vue
index e7e176d7..33474067 100644
--- a/ux/src/layouts/AdminLayout.vue
+++ b/ux/src/layouts/AdminLayout.vue
@@ -166,6 +166,10 @@ q-layout.admin(view='hHh Lpr lff')
q-item-section {{ t('admin.system.title') }}
q-item-section(side)
status-light(:color='adminStore.isVersionLatest ? `positive` : `warning`')
+ q-item(to='/_admin/terminal', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-linux-terminal.svg')
+ q-item-section {{ t('admin.terminal.title') }}
q-item(to='/_admin/utilities', v-ripple, active-class='bg-primary text-white')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-swiss-army-knife.svg')
diff --git a/ux/src/pages/AdminTerminal.vue b/ux/src/pages/AdminTerminal.vue
new file mode 100644
index 00000000..5e224896
--- /dev/null
+++ b/ux/src/pages/AdminTerminal.vue
@@ -0,0 +1,169 @@
+
+q-page.admin-terminal
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-linux-terminal-animated.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ t('admin.terminal.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.terminal.subtitle') }}
+ .col-auto.flex
+ q-btn.acrylic-btn.q-mr-sm(
+ v-if='!state.connected || state.connecting'
+ flat
+ icon='las la-link'
+ :label='t(`admin.terminal.connect`)'
+ color='positive'
+ @click='connect'
+ :loading='state.connecting'
+ :disabled='state.connecting'
+ )
+ q-btn.acrylic-btn.q-mr-sm(
+ v-else
+ flat
+ icon='las la-unlink'
+ :label='t(`admin.terminal.disconnect`)'
+ color='negative'
+ @click='disconnect'
+ )
+ q-btn.acrylic-btn.q-mr-md(
+ flat
+ icon='las la-ban'
+ :label='t(`admin.terminal.clear`)'
+ color='primary'
+ @click='clearTerminal'
+ )
+ q-separator.q-mr-md(vertical)
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ :href='siteStore.docsBase + `/admin/terminal`'
+ target='_blank'
+ type='a'
+ )
+ q-separator(inset)
+ .q-pa-md.q-gutter-md
+ q-card.shadow-1
+ .admin-terminal-term(ref='termDiv')
+
+
+
+
+
+
diff --git a/ux/src/pages/AdminUtilities.vue b/ux/src/pages/AdminUtilities.vue
index d3967a3c..64d7785a 100644
--- a/ux/src/pages/AdminUtilities.vue
+++ b/ux/src/pages/AdminUtilities.vue
@@ -19,6 +19,19 @@ q-page.admin-utilities
.q-pa-md.q-gutter-md
q-card.shadow-1
q-list(separator)
+ q-item
+ blueprint-icon(icon='disconnected', :hue-rotate='45')
+ q-item-section
+ q-item-label {{t(`admin.utilities.disconnectWS`)}}
+ q-item-label(caption) {{t(`admin.utilities.disconnectWSHint`)}}
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-arrow-circle-right'
+ color='primary'
+ @click='disconnectWS'
+ :label='t(`common.actions.proceed`)'
+ )
q-item
blueprint-icon(icon='database-export', :hue-rotate='45')
q-item-section
@@ -103,6 +116,7 @@ q-page.admin-utilities
import { computed, reactive } from 'vue'
import { useMeta, useQuasar } from 'quasar'
import { useI18n } from 'vue-i18n'
+import gql from 'graphql-tag'
import { useSiteStore } from 'src/stores/site'
@@ -140,6 +154,42 @@ const purgeHistoryTimeframes = computed(() => ([
{ value: '1y', label: t('admin.utitilies.purgeHistoryYear', 1, { count: 1 }) },
{ value: '2y', label: t('admin.utitilies.purgeHistoryYear', 2, { count: 2 }) }
]))
+
+// METHODS
+
+async function disconnectWS () {
+ $q.loading.show()
+ try {
+ const resp = await APOLLO_CLIENT.mutate({
+ mutation: gql`
+ mutation disconnectWS {
+ disconnectWS {
+ operation {
+ succeeded
+ message
+ }
+ }
+ }
+ `,
+ fetchPolicy: 'network-only'
+ })
+ if (resp?.data?.disconnectWS?.operation?.succeeded) {
+ $q.notify({
+ type: 'positive',
+ message: t('admin.utilities.disconnectWSSuccess')
+ })
+ } else {
+ throw new Error(resp?.data?.disconnectWS?.operation?.succeeded)
+ }
+ } catch (err) {
+ $q.notify({
+ type: 'negative',
+ message: 'Failed to disconnect WS connections.',
+ caption: err.message
+ })
+ }
+ $q.loading.hide()
+}