Unverified Commit 960a8a03 authored by NGPixel's avatar NGPixel

feat: update permission system + dark theme fixes + logout

parent 59f6b6fe
...@@ -39,7 +39,7 @@ export default { ...@@ -39,7 +39,7 @@ export default {
*/ */
async preBootWeb() { async preBootWeb() {
try { try {
WIKI.cache = new NodeCache() WIKI.cache = new NodeCache({ checkperiod: 0 })
WIKI.scheduler = await scheduler.init() WIKI.scheduler = await scheduler.init()
WIKI.servers = servers WIKI.servers = servers
WIKI.events = { WIKI.events = {
......
import { v4 as uuid } from 'uuid' import { v4 as uuid } from 'uuid'
import bcrypt from 'bcryptjs-then' import bcrypt from 'bcryptjs'
import crypto from 'node:crypto' import crypto from 'node:crypto'
import { DateTime } from 'luxon' import { DateTime } from 'luxon'
import { pem2jwk } from 'pem-jwk' import { pem2jwk } from 'pem-jwk'
......
...@@ -85,12 +85,24 @@ export default { ...@@ -85,12 +85,24 @@ export default {
.whereNotNull('lastLoginAt') .whereNotNull('lastLoginAt')
.orderBy('lastLoginAt', 'desc') .orderBy('lastLoginAt', 'desc')
.limit(10) .limit(10)
},
async userPermissions (obj, args, context) {
if (!context.req.user || context.req.user.id === WIKI.auth.guest.id) {
throw new WIKI.Error.AuthRequired()
}
const currentUser = await WIKI.db.users.getById(context.req.user.id)
return currentUser.getPermissions()
},
async userPermissionsAtPath (obj, args, context) {
return []
} }
}, },
Mutation: { Mutation: {
async createUser (obj, args) { async createUser (obj, args) {
try { try {
await WIKI.db.users.createNewUser({ ...args, passwordRaw: args.password, isVerified: true }) await WIKI.db.users.createNewUser({ ...args, isVerified: true })
return { return {
operation: generateSuccess('User created successfully') operation: generateSuccess('User created successfully')
......
...@@ -17,6 +17,13 @@ extend type Query { ...@@ -17,6 +17,13 @@ extend type Query {
): User ): User
lastLogins: [UserLastLogin] lastLogins: [UserLastLogin]
userPermissions: [String]
userPermissionsAtPath(
siteId: UUID!
path: String!
): [String]
} }
extend type Mutation { extend type Mutation {
......
...@@ -29,8 +29,8 @@ export class Authentication extends Model { ...@@ -29,8 +29,8 @@ export class Authentication extends Model {
return ['config', 'domainWhitelist', 'autoEnrollGroups'] return ['config', 'domainWhitelist', 'autoEnrollGroups']
} }
static async getStrategy(key) { static async getStrategy(module) {
return WIKI.db.authentication.query().findOne({ key }) return WIKI.db.authentication.query().findOne({ module })
} }
static async getStrategies({ enabledOnly = false } = {}) { static async getStrategies({ enabledOnly = false } = {}) {
......
...@@ -6,12 +6,11 @@ import jwt from 'jsonwebtoken' ...@@ -6,12 +6,11 @@ import jwt from 'jsonwebtoken'
import { Model } from 'objection' import { Model } from 'objection'
import validate from 'validate.js' import validate from 'validate.js'
import qr from 'qr-image' import qr from 'qr-image'
import bcrypt from 'bcryptjs'
import { Group } from './groups.mjs' import { Group } from './groups.mjs'
import { Locale } from './locales.mjs' import { Locale } from './locales.mjs'
const bcryptRegexp = /^\$2[ayb]\$[0-9]{2}\$[A-Za-z0-9./]{53}$/
/** /**
* Users model * Users model
*/ */
...@@ -70,18 +69,12 @@ export class User extends Model { ...@@ -70,18 +69,12 @@ export class User extends Model {
await super.$beforeUpdate(opt, context) await super.$beforeUpdate(opt, context)
this.updatedAt = new Date().toISOString() this.updatedAt = new Date().toISOString()
if (!(opt.patch && this.password === undefined)) {
await this.generateHash()
}
} }
async $beforeInsert(context) { async $beforeInsert(context) {
await super.$beforeInsert(context) await super.$beforeInsert(context)
this.createdAt = new Date().toISOString() this.createdAt = new Date().toISOString()
this.updatedAt = new Date().toISOString() this.updatedAt = new Date().toISOString()
await this.generateHash()
} }
// ------------------------------------------------ // ------------------------------------------------
...@@ -524,116 +517,104 @@ export class User extends Model { ...@@ -524,116 +517,104 @@ export class User extends Model {
* *
* @param {Object} param0 User Fields * @param {Object} param0 User Fields
*/ */
static async createNewUser ({ providerKey, email, passwordRaw, name, groups, mustChangePassword, sendWelcomeEmail }) { static async createNewUser ({ email, password, name, groups, mustChangePassword = false, sendWelcomeEmail = false }) {
// Input sanitization // Input sanitization
email = email.toLowerCase() email = email.toLowerCase().trim()
// Input validation // Input validation
let validation = null const validation = validate({
if (providerKey === 'local') { email,
validation = validate({ password,
email, name
passwordRaw, }, {
name email: {
}, { email: true,
email: { length: {
email: true, maximum: 255
length: { }
maximum: 255 },
} password: {
}, presence: {
passwordRaw: { allowEmpty: false
presence: {
allowEmpty: false
},
length: {
minimum: 6
}
}, },
name: { length: {
presence: { minimum: 6
allowEmpty: false
},
length: {
minimum: 2,
maximum: 255
}
} }
}, { format: 'flat' }) },
} else { name: {
validation = validate({ presence: {
email, allowEmpty: false
name
}, {
email: {
email: true,
length: {
maximum: 255
}
}, },
name: { length: {
presence: { minimum: 2,
allowEmpty: false maximum: 255
},
length: {
minimum: 2,
maximum: 255
}
} }
}, { format: 'flat' }) }
} }, { format: 'flat' })
if (validation && validation.length > 0) { if (validation && validation.length > 0) {
throw new WIKI.Error.InputInvalid(validation[0]) throw new Error(`ERR_INVALID_INPUT: ${validation[0]}`)
} }
// Check if email already exists // Check if email already exists
const usr = await WIKI.db.users.query().findOne({ email, providerKey }) const usr = await WIKI.db.users.query().findOne({ email })
if (!usr) { if (usr) {
// Create the account throw new Error('ERR_ACCOUNT_ALREADY_EXIST')
let newUsrData = { }
providerKey,
email,
name,
locale: 'en',
defaultEditor: 'markdown',
tfaIsActive: false,
isSystem: false,
isActive: true,
isVerified: true,
mustChangePwd: false
}
if (providerKey === `local`) { // Create the account
newUsrData.password = passwordRaw const localAuth = await WIKI.db.authentication.getStrategy('local')
newUsrData.mustChangePwd = (mustChangePassword === true) const newUsr = await WIKI.db.users.query().insert({
email,
name,
auth: {
[localAuth.id]: {
password: await bcrypt.hash(password, 12),
mustChangePwd: mustChangePassword,
restrictLogin: false,
tfaRequired: false,
tfaSecret: ''
}
},
localeCode: 'en',
hasAvatar: false,
isSystem: false,
isActive: true,
isVerified: true,
meta: {
jobTitle: '',
location: '',
pronouns: ''
},
prefs: {
cvd: 'none',
timezone: 'America/New_York',
appearance: 'site',
dateFormat: 'YYYY-MM-DD',
timeFormat: '12h'
} }
})
const newUsr = await WIKI.db.users.query().insert(newUsrData) // Assign to group(s)
if (groups.length > 0) {
await newUsr.$relatedQuery('groups').relate(groups)
}
// Assign to group(s) if (sendWelcomeEmail) {
if (groups.length > 0) { // Send welcome email
await newUsr.$relatedQuery('groups').relate(groups) await WIKI.mail.send({
} template: 'accountWelcome',
to: email,
if (sendWelcomeEmail) { subject: `Welcome to the wiki ${WIKI.config.title}`,
// Send welcome email data: {
await WIKI.mail.send({ preheadertext: `You've been invited to the wiki ${WIKI.config.title}`,
template: 'accountWelcome', title: `You've been invited to the wiki ${WIKI.config.title}`,
to: email, content: `Click the button below to access the wiki.`,
subject: `Welcome to the wiki ${WIKI.config.title}`, buttonLink: `${WIKI.config.host}/login`,
data: { buttonText: 'Login'
preheadertext: `You've been invited to the wiki ${WIKI.config.title}`, },
title: `You've been invited to the wiki ${WIKI.config.title}`, text: `You've been invited to the wiki ${WIKI.config.title}: ${WIKI.config.host}/login`
content: `Click the button below to access the wiki.`, })
buttonLink: `${WIKI.config.host}/login`,
buttonText: 'Login'
},
text: `You've been invited to the wiki ${WIKI.config.title}: ${WIKI.config.host}/login`
})
}
} else {
throw new WIKI.Error.AuthAccountAlreadyExists()
} }
} }
......
/* global WIKI */ /* global WIKI */
import bcrypt from 'bcryptjs-then' import bcrypt from 'bcryptjs'
// ------------------------------------ // ------------------------------------
// Local Account // Local Account
......
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
"apollo-server-express": "3.6.7", "apollo-server-express": "3.6.7",
"auto-load": "3.0.4", "auto-load": "3.0.4",
"aws-sdk": "2.1353.0", "aws-sdk": "2.1353.0",
"bcryptjs-then": "1.0.1", "bcryptjs": "2.4.3",
"body-parser": "1.20.2", "body-parser": "1.20.2",
"chalk": "5.2.0", "chalk": "5.2.0",
"cheerio": "1.0.0-rc.12", "cheerio": "1.0.0-rc.12",
......
...@@ -9,6 +9,7 @@ import { useFlagsStore } from 'src/stores/flags' ...@@ -9,6 +9,7 @@ import { useFlagsStore } from 'src/stores/flags'
import { useSiteStore } from 'src/stores/site' import { useSiteStore } from 'src/stores/site'
import { useUserStore } from 'src/stores/user' import { useUserStore } from 'src/stores/user'
import { setCssVar, useQuasar } from 'quasar' import { setCssVar, useQuasar } from 'quasar'
import { useI18n } from 'vue-i18n'
import '@mdi/font/css/materialdesignicons.css' import '@mdi/font/css/materialdesignicons.css'
...@@ -24,6 +25,10 @@ const flagsStore = useFlagsStore() ...@@ -24,6 +25,10 @@ const flagsStore = useFlagsStore()
const siteStore = useSiteStore() const siteStore = useSiteStore()
const userStore = useUserStore() const userStore = useUserStore()
// I18N
const { t } = useI18n()
// ROUTER // ROUTER
const router = useRouter() const router = useRouter()
...@@ -94,14 +99,33 @@ router.beforeEach(async (to, from) => { ...@@ -94,14 +99,33 @@ router.beforeEach(async (to, from) => {
console.info(`Refreshing user ${userStore.id} profile...`) console.info(`Refreshing user ${userStore.id} profile...`)
await userStore.refreshProfile() await userStore.refreshProfile()
} }
// Apply Theme // Page Permissions
await userStore.fetchPagePermissions(to.path)
})
// GLOBAL EVENTS HANDLERS
EVENT_BUS.on('logout', () => {
router.push('/')
$q.notify({
type: 'positive',
icon: 'las la-sign-out-alt',
message: t('auth.logoutSuccess')
})
})
EVENT_BUS.on('applyTheme', () => {
applyTheme() applyTheme()
}) })
// LOADER
router.afterEach(() => { router.afterEach(() => {
if (!state.isInitialized) { if (!state.isInitialized) {
state.isInitialized = true state.isInitialized = true
applyTheme()
document.querySelector('.init-loading').remove() document.querySelector('.init-loading').remove()
} }
siteStore.routerLoading = false siteStore.routerLoading = false
}) })
</script> </script>
...@@ -28,7 +28,7 @@ q-btn.q-ml-md(flat, round, dense, color='grey') ...@@ -28,7 +28,7 @@ q-btn.q-ml-md(flat, round, dense, color='grey')
:label='t(`common.header.logout`)' :label='t(`common.header.logout`)'
icon='las la-sign-out-alt' icon='las la-sign-out-alt'
color='red' color='red'
href='/logout' @click='userStore.logout()'
no-caps no-caps
) )
q-tooltip {{ t('common.header.account') }} q-tooltip {{ t('common.header.account') }}
......
...@@ -584,50 +584,43 @@ const usersHeaders = [ ...@@ -584,50 +584,43 @@ const usersHeaders = [
const permissions = [ const permissions = [
{ {
permission: 'write:users', permission: 'access:admin',
hint: 'Can create or authorize new users, but not modify existing ones', hint: 'Can access the administration area.',
warning: false, warning: false,
restrictedForSystem: true, restrictedForSystem: true,
disabled: false disabled: false
}, },
{ {
permission: 'manage:users', permission: 'manage:users',
hint: 'Can manage all users (but not users with administrative permissions)', hint: 'Can create / manage users (but not users with administrative permissions)',
warning: false,
restrictedForSystem: true,
disabled: false
},
{
permission: 'write:groups',
hint: 'Can manage groups and assign CONTENT permissions / page rules',
warning: false, warning: false,
restrictedForSystem: true, restrictedForSystem: true,
disabled: false disabled: false
}, },
{ {
permission: 'manage:groups', permission: 'manage:groups',
hint: 'Can manage groups and assign ANY permissions (but not manage:system) / page rules', hint: 'Can create / manage groups and assign permissions (but not manage:system) / page rules',
warning: true, warning: true,
restrictedForSystem: true, restrictedForSystem: true,
disabled: false disabled: false
}, },
{ {
permission: 'manage:navigation', permission: 'manage:navigation',
hint: 'Can manage the site navigation', hint: 'Can manage site navigation',
warning: false, warning: false,
restrictedForSystem: true, restrictedForSystem: true,
disabled: false disabled: false
}, },
{ {
permission: 'manage:theme', permission: 'manage:theme',
hint: 'Can manage and modify themes', hint: 'Can modify site theme settings',
warning: false, warning: false,
restrictedForSystem: true, restrictedForSystem: true,
disabled: false disabled: false
}, },
{ {
permission: 'manage:api', permission: 'manage:sites',
hint: 'Can generate and revoke API keys', hint: 'Can create / manage sites',
warning: true, warning: true,
restrictedForSystem: true, restrictedForSystem: true,
disabled: false disabled: false
......
...@@ -73,6 +73,7 @@ q-header.bg-header.text-white.site-header( ...@@ -73,6 +73,7 @@ q-header.bg-header.text-white.site-header(
size='24px' size='24px'
) )
q-btn.q-ml-md( q-btn.q-ml-md(
v-if='userStore.can(`write:pages`)'
flat flat
round round
dense dense
...@@ -83,6 +84,7 @@ q-header.bg-header.text-white.site-header( ...@@ -83,6 +84,7 @@ q-header.bg-header.text-white.site-header(
q-tooltip Create New Page q-tooltip Create New Page
new-menu new-menu
q-btn.q-ml-md( q-btn.q-ml-md(
v-if='userStore.can(`browse:fileman`)'
flat flat
round round
dense dense
...@@ -93,6 +95,7 @@ q-header.bg-header.text-white.site-header( ...@@ -93,6 +95,7 @@ q-header.bg-header.text-white.site-header(
) )
q-tooltip File Manager q-tooltip File Manager
q-btn.q-ml-md( q-btn.q-ml-md(
v-if='userStore.can(`access:admin`)'
flat flat
round round
dense dense
...@@ -102,7 +105,21 @@ q-header.bg-header.text-white.site-header( ...@@ -102,7 +105,21 @@ q-header.bg-header.text-white.site-header(
:aria-label='t(`common.header.admin`)' :aria-label='t(`common.header.admin`)'
) )
q-tooltip {{ t('common.header.admin') }} q-tooltip {{ t('common.header.admin') }}
account-menu
//- USER BUTTON / DROPDOWN
account-menu(v-if='userStore.authenticated')
q-btn.q-ml-md(
v-else
flat
rounded
icon='las la-sign-in-alt'
color='white'
:label='$t(`common.actions.login`)'
:aria-label='$t(`common.actions.login`)'
to='/login'
padding='sm'
no-caps
)
</template> </template>
<script setup> <script setup>
...@@ -114,6 +131,7 @@ import { useQuasar } from 'quasar' ...@@ -114,6 +131,7 @@ import { useQuasar } from 'quasar'
import { reactive } from 'vue' import { reactive } from 'vue'
import { useSiteStore } from 'src/stores/site' import { useSiteStore } from 'src/stores/site'
import { useUserStore } from 'src/stores/user'
// QUASAR // QUASAR
...@@ -122,6 +140,7 @@ const $q = useQuasar() ...@@ -122,6 +140,7 @@ const $q = useQuasar()
// STORES // STORES
const siteStore = useSiteStore() const siteStore = useSiteStore()
const userStore = useUserStore()
// I18N // I18N
......
<template lang="pug"> <template lang="pug">
.page-actions.column.items-stretch.order-last(:class='editorStore.isActive ? `is-editor` : ``') .page-actions.column.items-stretch.order-last(:class='editorStore.isActive ? `is-editor` : ``')
template(v-if='userStore.can(`edit:pages`)')
q-btn.q-py-md(
flat
icon='las la-pen-nib'
:color='editorStore.isActive ? `white` : `deep-orange-9`'
aria-label='Page Properties'
@click='togglePageProperties'
)
q-tooltip(anchor='center left' self='center right') Page Properties
q-btn.q-py-md(
v-if='flagsStore.experimental'
flat
icon='las la-project-diagram'
:color='editorStore.isActive ? `white` : `deep-orange-9`'
aria-label='Page Data'
@click='togglePageData'
disable
)
q-tooltip(anchor='center left' self='center right') Page Data
q-separator.q-my-sm(inset)
q-btn.q-py-md( q-btn.q-py-md(
flat flat
icon='las la-pen-nib' icon='las la-history'
:color='editorStore.isActive ? `white` : `deep-orange-9`' :color='editorStore.isActive ? `white` : `grey`'
aria-label='Page Properties' aria-label='Page History'
@click='togglePageProperties'
) )
q-tooltip(anchor='center left' self='center right') Page Properties q-tooltip(anchor='center left' self='center right') Page History
q-btn.q-py-md( q-btn.q-py-md(
flat flat
icon='las la-project-diagram' icon='las la-code'
:color='editorStore.isActive ? `white` : `deep-orange-9`' :color='editorStore.isActive ? `white` : `grey`'
aria-label='Page Data' aria-label='Page Source'
@click='togglePageData'
disable
v-if='flagsStore.experimental'
) )
q-tooltip(anchor='center left' self='center right') Page Data q-tooltip(anchor='center left' self='center right') Page Source
template(v-if='!(editorStore.isActive && editorStore.mode === `create`)') template(v-if='!(editorStore.isActive && editorStore.mode === `create`)')
q-separator.q-my-sm(inset) q-separator.q-my-sm(inset)
q-btn.q-py-sm( q-btn.q-py-sm(
...@@ -34,22 +50,12 @@ ...@@ -34,22 +50,12 @@
transition-show='jump-left' transition-show='jump-left'
) )
q-list(padding, style='min-width: 225px;') q-list(padding, style='min-width: 225px;')
q-item(clickable) q-item(clickable, v-if='userStore.can(`manage:pages`)')
q-item-section.items-center(avatar)
q-icon(color='deep-orange-9', name='las la-history', size='sm')
q-item-section
q-item-label View History
q-item(clickable)
q-item-section.items-center(avatar)
q-icon(color='deep-orange-9', name='las la-code', size='sm')
q-item-section
q-item-label View Source
q-item(clickable)
q-item-section.items-center(avatar) q-item-section.items-center(avatar)
q-icon(color='deep-orange-9', name='las la-atom', size='sm') q-icon(color='deep-orange-9', name='las la-atom', size='sm')
q-item-section q-item-section
q-item-label Convert Page q-item-label Convert Page
q-item(clickable) q-item(clickable, v-if='userStore.can(`edit:pages`)')
q-item-section.items-center(avatar) q-item-section.items-center(avatar)
q-icon(color='deep-orange-9', name='las la-magic', size='sm') q-icon(color='deep-orange-9', name='las la-magic', size='sm')
q-item-section q-item-section
...@@ -62,6 +68,7 @@ ...@@ -62,6 +68,7 @@
q-space q-space
template(v-if='!(editorStore.isActive && editorStore.mode === `create`)') template(v-if='!(editorStore.isActive && editorStore.mode === `create`)')
q-btn.q-py-sm( q-btn.q-py-sm(
v-if='userStore.can(`create:pages`)'
flat flat
icon='las la-copy' icon='las la-copy'
:color='editorStore.isActive ? `deep-orange-2` : `grey`' :color='editorStore.isActive ? `deep-orange-2` : `grey`'
...@@ -70,6 +77,7 @@ ...@@ -70,6 +77,7 @@
) )
q-tooltip(anchor='center left' self='center right') Duplicate Page q-tooltip(anchor='center left' self='center right') Duplicate Page
q-btn.q-py-sm( q-btn.q-py-sm(
v-if='userStore.can(`manage:pages`)'
flat flat
icon='las la-share' icon='las la-share'
:color='editorStore.isActive ? `deep-orange-2` : `grey`' :color='editorStore.isActive ? `deep-orange-2` : `grey`'
...@@ -78,6 +86,7 @@ ...@@ -78,6 +86,7 @@
) )
q-tooltip(anchor='center left' self='center right') Rename / Move Page q-tooltip(anchor='center left' self='center right') Rename / Move Page
q-btn.q-py-sm( q-btn.q-py-sm(
v-if='userStore.can(`delete:pages`)'
flat flat
icon='las la-trash' icon='las la-trash'
:color='editorStore.isActive ? `deep-orange-2` : `grey`' :color='editorStore.isActive ? `deep-orange-2` : `grey`'
...@@ -99,6 +108,7 @@ import { useEditorStore } from 'src/stores/editor' ...@@ -99,6 +108,7 @@ import { useEditorStore } from 'src/stores/editor'
import { useFlagsStore } from 'src/stores/flags' import { useFlagsStore } from 'src/stores/flags'
import { usePageStore } from 'src/stores/page' import { usePageStore } from 'src/stores/page'
import { useSiteStore } from 'src/stores/site' import { useSiteStore } from 'src/stores/site'
import { useUserStore } from 'src/stores/user'
// QUASAR // QUASAR
...@@ -110,6 +120,7 @@ const editorStore = useEditorStore() ...@@ -110,6 +120,7 @@ const editorStore = useEditorStore()
const flagsStore = useFlagsStore() const flagsStore = useFlagsStore()
const pageStore = usePageStore() const pageStore = usePageStore()
const siteStore = useSiteStore() const siteStore = useSiteStore()
const userStore = useUserStore()
// ROUTER // ROUTER
......
...@@ -73,7 +73,8 @@ ...@@ -73,7 +73,8 @@
//- PAGE ACTIONS //- PAGE ACTIONS
.col-auto.q-pa-md.flex.items-center.justify-end .col-auto.q-pa-md.flex.items-center.justify-end
template(v-if='!editorStore.isActive') template(v-if='!editorStore.isActive')
q-btn.q-mr-md( q-btn.q-ml-md(
v-if='userStore.authenticated'
flat flat
dense dense
icon='las la-bell' icon='las la-bell'
...@@ -81,7 +82,8 @@ ...@@ -81,7 +82,8 @@
aria-label='Watch Page' aria-label='Watch Page'
) )
q-tooltip Watch Page q-tooltip Watch Page
q-btn.q-mr-md( q-btn.q-ml-md(
v-if='userStore.authenticated'
flat flat
dense dense
icon='las la-bookmark' icon='las la-bookmark'
...@@ -89,7 +91,7 @@ ...@@ -89,7 +91,7 @@
aria-label='Bookmark Page' aria-label='Bookmark Page'
) )
q-tooltip Bookmark Page q-tooltip Bookmark Page
q-btn.q-mr-md( q-btn.q-ml-md(
flat flat
dense dense
icon='las la-share-alt' icon='las la-share-alt'
...@@ -98,7 +100,7 @@ ...@@ -98,7 +100,7 @@
) )
q-tooltip Share q-tooltip Share
social-sharing-menu social-sharing-menu
q-btn.q-mr-md( q-btn.q-ml-md(
flat flat
dense dense
icon='las la-print' icon='las la-print'
...@@ -107,7 +109,7 @@ ...@@ -107,7 +109,7 @@
) )
q-tooltip Print q-tooltip Print
template(v-if='editorStore.isActive') template(v-if='editorStore.isActive')
q-btn.q-mr-sm.acrylic-btn( q-btn.q-ml-md.acrylic-btn(
icon='las la-question-circle' icon='las la-question-circle'
flat flat
color='grey' color='grey'
...@@ -115,7 +117,7 @@ ...@@ -115,7 +117,7 @@
target='_blank' target='_blank'
type='a' type='a'
) )
q-btn.q-mr-sm.acrylic-btn( q-btn.q-ml-sm.acrylic-btn(
icon='las la-cog' icon='las la-cog'
flat flat
color='grey' color='grey'
...@@ -123,7 +125,7 @@ ...@@ -123,7 +125,7 @@
@click='openEditorSettings' @click='openEditorSettings'
) )
template(v-if='editorStore.isActive || editorStore.hasPendingChanges') template(v-if='editorStore.isActive || editorStore.hasPendingChanges')
q-btn.acrylic-btn.q-mr-sm( q-btn.acrylic-btn.q-ml-sm(
flat flat
icon='las la-times' icon='las la-times'
color='negative' color='negative'
...@@ -132,7 +134,7 @@ ...@@ -132,7 +134,7 @@
no-caps no-caps
@click='discardChanges' @click='discardChanges'
) )
q-btn.acrylic-btn( q-btn.acrylic-btn.q-ml-sm(
v-if='editorStore.mode === `create`' v-if='editorStore.mode === `create`'
flat flat
icon='las la-check' icon='las la-check'
...@@ -142,7 +144,7 @@ ...@@ -142,7 +144,7 @@
no-caps no-caps
@click='createPage' @click='createPage'
) )
q-btn.acrylic-btn( q-btn.acrylic-btn.q-ml-sm(
v-else v-else
flat flat
icon='las la-check' icon='las la-check'
...@@ -153,8 +155,8 @@ ...@@ -153,8 +155,8 @@
no-caps no-caps
@click='saveChanges' @click='saveChanges'
) )
template(v-else) template(v-else-if='userStore.can(`edit:pages`)')
q-btn.acrylic-btn( q-btn.acrylic-btn.q-ml-md(
flat flat
icon='las la-edit' icon='las la-edit'
color='deep-orange-9' color='deep-orange-9'
...@@ -292,6 +294,31 @@ async function saveChangesCommit () { ...@@ -292,6 +294,31 @@ async function saveChangesCommit () {
} }
async function createPage () { async function createPage () {
// Handle home page creation flow
if (pageStore.path === 'home') {
$q.loading.show()
try {
await pageStore.pageSave()
$q.notify({
type: 'positive',
message: 'Homepage created successfully.'
})
editorStore.$patch({
isActive: false
})
router.replace('/')
} catch (err) {
$q.notify({
type: 'negative',
message: 'Failed to create homepage.',
caption: err.message
})
}
$q.loading.hide()
return
}
// All other pages
$q.dialog({ $q.dialog({
component: defineAsyncComponent(() => import('../components/TreeBrowserDialog.vue')), component: defineAsyncComponent(() => import('../components/TreeBrowserDialog.vue')),
componentProps: { componentProps: {
......
...@@ -91,6 +91,11 @@ body::-webkit-scrollbar-thumb { ...@@ -91,6 +91,11 @@ body::-webkit-scrollbar-thumb {
} }
} }
.q-field--dark .q-field__control:before {
background-color: $dark-5;
border-color: rgba(255,255,255,.25);
}
// ------------------------------------------------------------------ // ------------------------------------------------------------------
// ICONS SIZE FIX // ICONS SIZE FIX
// ------------------------------------------------------------------ // ------------------------------------------------------------------
...@@ -192,6 +197,22 @@ body::-webkit-scrollbar-thumb { ...@@ -192,6 +197,22 @@ body::-webkit-scrollbar-thumb {
} }
// ------------------------------------------------------------------ // ------------------------------------------------------------------
// CARDS
// ------------------------------------------------------------------
.q-card {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2), 0 1px 1px rgba(0, 0, 0, 0.14), 0 2px 1px -1px rgba(0, 0, 0, 0.12);
&.q-card--dark {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2), 0 1px 1px rgba(0, 0, 0, 0.14), 0 2px 1px -1px rgba(0, 0, 0, 0.12);
}
.q-separator--dark {
background-color: rgba(0,0,0,.7);
}
}
// ------------------------------------------------------------------
// DROPDOWN MENUS // DROPDOWN MENUS
// ------------------------------------------------------------------ // ------------------------------------------------------------------
...@@ -210,6 +231,10 @@ body::-webkit-scrollbar-thumb { ...@@ -210,6 +231,10 @@ body::-webkit-scrollbar-thumb {
} }
} }
.q-menu--dark {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2), 0 1px 1px rgba(0, 0, 0, 0.14), 0 2px 1px -1px rgba(0, 0, 0, 0.12);
}
// ------------------------------------------------------------------ // ------------------------------------------------------------------
// LOADING ANIMATIONS // LOADING ANIMATIONS
// ------------------------------------------------------------------ // ------------------------------------------------------------------
......
...@@ -133,7 +133,7 @@ ...@@ -133,7 +133,7 @@
&::before { &::before {
display: inline-block; display: inline-block;
font: normal normal normal 24px/1 "Line Awesome Free", sans-serif; font: normal normal normal 24px/1 "Material Design Icons", sans-serif;
position: absolute; position: absolute;
margin-top: -12px; margin-top: -12px;
top: 50%; top: 50%;
......
...@@ -1129,6 +1129,7 @@ ...@@ -1129,6 +1129,7 @@
"auth.loginRequired": "Login required", "auth.loginRequired": "Login required",
"auth.loginSuccess": "Login Successful! Redirecting...", "auth.loginSuccess": "Login Successful! Redirecting...",
"auth.loginUsingStrategy": "Login using {strategy}", "auth.loginUsingStrategy": "Login using {strategy}",
"auth.logoutSuccess": "You've been logged out successfully.",
"auth.missingEmail": "Missing email address.", "auth.missingEmail": "Missing email address.",
"auth.missingName": "Name is missing.", "auth.missingName": "Name is missing.",
"auth.missingPassword": "Missing password.", "auth.missingPassword": "Missing password.",
...@@ -1179,6 +1180,7 @@ ...@@ -1179,6 +1180,7 @@
"common.actions.generate": "Generate", "common.actions.generate": "Generate",
"common.actions.howItWorks": "How it works", "common.actions.howItWorks": "How it works",
"common.actions.insert": "Insert", "common.actions.insert": "Insert",
"common.actions.login": "Login",
"common.actions.manage": "Manage", "common.actions.manage": "Manage",
"common.actions.move": "Move", "common.actions.move": "Move",
"common.actions.moveTo": "Move To", "common.actions.moveTo": "Move To",
......
...@@ -27,7 +27,6 @@ q-layout(view='hHh Lpr lff') ...@@ -27,7 +27,6 @@ q-layout(view='hHh Lpr lff')
label='Browse' label='Browse'
aria-label='Browse' aria-label='Browse'
size='sm' size='sm'
@click='openFileManager'
) )
q-scroll-area.sidebar-nav( q-scroll-area.sidebar-nav(
:thumb-style='thumbStyle' :thumb-style='thumbStyle'
...@@ -38,21 +37,21 @@ q-layout(view='hHh Lpr lff') ...@@ -38,21 +37,21 @@ q-layout(view='hHh Lpr lff')
dense dense
dark dark
) )
q-item-label.text-blue-2.text-caption(header) Getting Started q-item-label.text-blue-2.text-caption(header) Header
q-item(to='/install') q-item(to='/install')
q-item-section(side) q-item-section(side)
q-icon(name='las la-dog', color='white') q-icon(name='las la-dog', color='white')
q-item-section Requirements q-item-section Link 1
q-item(to='/install') q-item(to='/install')
q-item-section(side) q-item-section(side)
q-icon(name='las la-cat', color='white') q-icon(name='las la-cat', color='white')
q-item-section Installation q-item-section Link 2
q-separator.q-my-sm(dark) q-separator.q-my-sm(dark)
q-item(to='/install') q-item(to='/install')
q-item-section(side) q-item-section(side)
q-icon(name='las la-cat', color='white') q-icon(name='mdi-fruit-grapes', color='white')
q-item-section Installation q-item-section Link 3
q-bar.bg-blue-9.text-white(dense, v-if='flagsStore.experimental') q-bar.bg-blue-9.text-white(dense, v-if='flagsStore.experimental && userStore.authenticated')
q-btn.col( q-btn.col(
icon='las la-dharmachakra' icon='las la-dharmachakra'
label='History' label='History'
...@@ -90,6 +89,7 @@ import { useI18n } from 'vue-i18n' ...@@ -90,6 +89,7 @@ import { useI18n } from 'vue-i18n'
import { useEditorStore } from 'src/stores/editor' import { useEditorStore } from 'src/stores/editor'
import { useFlagsStore } from 'src/stores/flags' import { useFlagsStore } from 'src/stores/flags'
import { useSiteStore } from 'src/stores/site' import { useSiteStore } from 'src/stores/site'
import { useUserStore } from 'src/stores/user'
// COMPONENTS // COMPONENTS
...@@ -106,6 +106,7 @@ const $q = useQuasar() ...@@ -106,6 +106,7 @@ const $q = useQuasar()
const editorStore = useEditorStore() const editorStore = useEditorStore()
const flagsStore = useFlagsStore() const flagsStore = useFlagsStore()
const siteStore = useSiteStore() const siteStore = useSiteStore()
const userStore = useUserStore()
// ROUTER // ROUTER
......
...@@ -32,7 +32,7 @@ q-layout(view='hHh Lpr lff') ...@@ -32,7 +32,7 @@ q-layout(view='hHh Lpr lff')
q-item( q-item(
clickable clickable
v-ripple v-ripple
href='/logout' @click='userStore.logout()'
) )
q-item-section(side) q-item-section(side)
q-icon(name='las la-sign-out-alt', color='negative') q-icon(name='las la-sign-out-alt', color='negative')
......
...@@ -73,7 +73,7 @@ q-page.admin-mail ...@@ -73,7 +73,7 @@ q-page.admin-mail
q-item-label: strong {{str.title}} q-item-label: strong {{str.title}}
q-item-label(caption, lines='2') {{str.description}} q-item-label(caption, lines='2') {{str.description}}
.col .col
q-card.shadow-1.q-pb-sm q-card.q-pb-sm
q-card-section q-card-section
.text-subtitle1 {{t('admin.storage.contentTypes')}} .text-subtitle1 {{t('admin.storage.contentTypes')}}
.text-body2.text-grey {{ t('admin.storage.contentTypesHint') }} .text-body2.text-grey {{ t('admin.storage.contentTypesHint') }}
...@@ -81,7 +81,7 @@ q-page.admin-mail ...@@ -81,7 +81,7 @@ q-page.admin-mail
//- ----------------------- //- -----------------------
//- Configuration //- Configuration
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm.q-mt-md q-card.q-pb-sm.q-mt-md
q-card-section q-card-section
.text-subtitle1 {{t('admin.storage.config')}} .text-subtitle1 {{t('admin.storage.config')}}
q-banner.q-mt-md( q-banner.q-mt-md(
......
...@@ -8,7 +8,7 @@ q-page.admin-dashboard ...@@ -8,7 +8,7 @@ q-page.admin-dashboard
.text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.dashboard.subtitle') }} .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.dashboard.subtitle') }}
.row.q-px-md.q-col-gutter-md .row.q-px-md.q-col-gutter-md
.col-12.col-sm-6.col-lg-3 .col-12.col-sm-6.col-lg-3
q-card.shadow-1 q-card
q-card-section.admin-dashboard-card q-card-section.admin-dashboard-card
img(src='/_assets/icons/fluent-change-theme.svg') img(src='/_assets/icons/fluent-change-theme.svg')
div div
...@@ -21,6 +21,7 @@ q-page.admin-dashboard ...@@ -21,6 +21,7 @@ q-page.admin-dashboard
color='primary' color='primary'
icon='las la-plus-circle' icon='las la-plus-circle'
:label='t(`common.actions.new`)' :label='t(`common.actions.new`)'
:disable='!userStore.can(`manage:sites`)'
@click='newSite' @click='newSite'
) )
q-separator.q-mx-sm(vertical) q-separator.q-mx-sm(vertical)
...@@ -29,10 +30,11 @@ q-page.admin-dashboard ...@@ -29,10 +30,11 @@ q-page.admin-dashboard
color='primary' color='primary'
icon='las la-sitemap' icon='las la-sitemap'
:label='t(`common.actions.manage`)' :label='t(`common.actions.manage`)'
:disable='!userStore.can(`manage:sites`)'
to='/_admin/sites' to='/_admin/sites'
) )
.col-12.col-sm-6.col-lg-3 .col-12.col-sm-6.col-lg-3
q-card.shadow-1 q-card
q-card-section.admin-dashboard-card q-card-section.admin-dashboard-card
img(src='/_assets/icons/fluent-account.svg') img(src='/_assets/icons/fluent-account.svg')
div div
...@@ -45,6 +47,7 @@ q-page.admin-dashboard ...@@ -45,6 +47,7 @@ q-page.admin-dashboard
color='primary' color='primary'
icon='las la-user-plus' icon='las la-user-plus'
:label='t(`common.actions.new`)' :label='t(`common.actions.new`)'
:disable='!userStore.can(`manage:users`)'
@click='newUser' @click='newUser'
) )
q-separator.q-mx-sm(vertical) q-separator.q-mx-sm(vertical)
...@@ -53,10 +56,11 @@ q-page.admin-dashboard ...@@ -53,10 +56,11 @@ q-page.admin-dashboard
color='primary' color='primary'
icon='las la-users' icon='las la-users'
:label='t(`common.actions.manage`)' :label='t(`common.actions.manage`)'
:disable='!userStore.can(`manage:users`)'
to='/_admin/users' to='/_admin/users'
) )
.col-12.col-sm-6.col-lg-3 .col-12.col-sm-6.col-lg-3
q-card.shadow-1 q-card
q-card-section.admin-dashboard-card q-card-section.admin-dashboard-card
img(src='/_assets/icons/fluent-female-working-with-a-laptop.svg') img(src='/_assets/icons/fluent-female-working-with-a-laptop.svg')
div div
...@@ -69,10 +73,11 @@ q-page.admin-dashboard ...@@ -69,10 +73,11 @@ q-page.admin-dashboard
color='primary' color='primary'
icon='las la-chart-area' icon='las la-chart-area'
:label='t(`admin.analytics.title`)' :label='t(`admin.analytics.title`)'
:disable='!userStore.can(`manage:sites`)'
:to='`/_admin/` + adminStore.currentSiteId + `/analytics`' :to='`/_admin/` + adminStore.currentSiteId + `/analytics`'
) )
.col-12.col-sm-6.col-lg-3 .col-12.col-sm-6.col-lg-3
q-card.shadow-1 q-card
q-card-section.admin-dashboard-card q-card-section.admin-dashboard-card
img(src='/_assets/icons/fluent-ssd-animated.svg') img(src='/_assets/icons/fluent-ssd-animated.svg')
div div
...@@ -85,6 +90,7 @@ q-page.admin-dashboard ...@@ -85,6 +90,7 @@ q-page.admin-dashboard
color='primary' color='primary'
icon='las la-server' icon='las la-server'
:label='t(`common.actions.manage`)' :label='t(`common.actions.manage`)'
:disable='!userStore.can(`manage:sites`)'
:to='`/_admin/` + adminStore.currentSiteId + `/storage`' :to='`/_admin/` + adminStore.currentSiteId + `/storage`'
) )
.col-12 .col-12
...@@ -96,7 +102,7 @@ q-page.admin-dashboard ...@@ -96,7 +102,7 @@ q-page.admin-dashboard
i.las.la-check.q-mr-sm i.las.la-check.q-mr-sm
span.text-weight-medium(v-if='adminStore.isVersionLatest') Your Wiki.js server is running the latest version! span.text-weight-medium(v-if='adminStore.isVersionLatest') Your Wiki.js server is running the latest version!
span.text-weight-medium(v-else) A new version of Wiki.js is available. Please update to the latest version. span.text-weight-medium(v-else) A new version of Wiki.js is available. Please update to the latest version.
template(#action) template(#action, v-if='userStore.can(`manage:system`)')
q-btn( q-btn(
flat flat
:label='t(`admin.system.checkForUpdates`)' :label='t(`admin.system.checkForUpdates`)'
...@@ -109,7 +115,7 @@ q-page.admin-dashboard ...@@ -109,7 +115,7 @@ q-page.admin-dashboard
to='/_admin/system' to='/_admin/system'
) )
.col-12 .col-12
q-card.shadow-1 q-card
q-card-section --- q-card-section ---
//- v-container(fluid, grid-list-lg) //- v-container(fluid, grid-list-lg)
...@@ -224,6 +230,7 @@ import { useI18n } from 'vue-i18n' ...@@ -224,6 +230,7 @@ import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useAdminStore } from '../stores/admin' import { useAdminStore } from '../stores/admin'
import { useUserStore } from 'src/stores/user'
// COMPONENTS // COMPONENTS
...@@ -238,6 +245,7 @@ const $q = useQuasar() ...@@ -238,6 +245,7 @@ const $q = useQuasar()
// STORES // STORES
const adminStore = useAdminStore() const adminStore = useAdminStore()
const userStore = useUserStore()
// ROUTER // ROUTER
......
...@@ -32,7 +32,7 @@ q-page.admin-flags ...@@ -32,7 +32,7 @@ q-page.admin-flags
) )
q-separator(inset) q-separator(inset)
.q-pa-md.q-gutter-md .q-pa-md.q-gutter-md
q-card.shadow-1 q-card
q-list(separator) q-list(separator)
template(v-for='editor of editors', :key='editor.id') template(v-for='editor of editors', :key='editor.id')
q-item(v-if='flagsStore.experimental || !editor.isDisabled') q-item(v-if='flagsStore.experimental || !editor.isDisabled')
......
...@@ -25,7 +25,7 @@ q-page.admin-extensions ...@@ -25,7 +25,7 @@ q-page.admin-extensions
q-separator(inset) q-separator(inset)
.row.q-pa-md.q-col-gutter-md .row.q-pa-md.q-col-gutter-md
.col-12 .col-12
q-card.shadow-1 q-card
q-list(separator) q-list(separator)
q-item( q-item(
v-for='(ext, idx) of state.extensions' v-for='(ext, idx) of state.extensions'
......
...@@ -33,7 +33,7 @@ q-page.admin-flags ...@@ -33,7 +33,7 @@ q-page.admin-flags
q-separator(inset) q-separator(inset)
.row.q-pa-md.q-col-gutter-md .row.q-pa-md.q-col-gutter-md
.col-12.col-lg-7 .col-12.col-lg-7
q-card.shadow-1.q-py-sm q-card.q-py-sm
q-item q-item
q-item-section q-item-section
q-card.bg-negative.text-white.rounded-borders(flat) q-card.bg-negative.text-white.rounded-borders(flat)
...@@ -84,7 +84,7 @@ q-page.admin-flags ...@@ -84,7 +84,7 @@ q-page.admin-flags
unchecked-icon='las la-times' unchecked-icon='las la-times'
:aria-label='t(`admin.flags.sqlLog.label`)' :aria-label='t(`admin.flags.sqlLog.label`)'
) )
q-card.shadow-1.q-py-sm.q-mt-md q-card.q-py-sm.q-mt-md
q-item q-item
blueprint-icon(icon='administrative-tools') blueprint-icon(icon='administrative-tools')
q-item-section q-item-section
......
...@@ -36,7 +36,7 @@ q-page.admin-general ...@@ -36,7 +36,7 @@ q-page.admin-general
//- ----------------------- //- -----------------------
//- Site Info //- Site Info
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm q-card.q-pb-sm
q-card-section q-card-section
.text-subtitle1 {{t('admin.general.siteInfo')}} .text-subtitle1 {{t('admin.general.siteInfo')}}
q-item q-item
...@@ -85,7 +85,7 @@ q-page.admin-general ...@@ -85,7 +85,7 @@ q-page.admin-general
//- ----------------------- //- -----------------------
//- Footer / Copyright //- Footer / Copyright
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm.q-mt-md q-card.q-pb-sm.q-mt-md
q-card-section q-card-section
.text-subtitle1 {{t('admin.general.footerCopyright')}} .text-subtitle1 {{t('admin.general.footerCopyright')}}
q-item q-item
...@@ -135,7 +135,7 @@ q-page.admin-general ...@@ -135,7 +135,7 @@ q-page.admin-general
//- ----------------------- //- -----------------------
//- FEATURES //- FEATURES
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm.q-mt-md q-card.q-pb-sm.q-mt-md
q-card-section q-card-section
.text-subtitle1 {{t('admin.general.features')}} .text-subtitle1 {{t('admin.general.features')}}
q-item(tag='label') q-item(tag='label')
...@@ -227,7 +227,7 @@ q-page.admin-general ...@@ -227,7 +227,7 @@ q-page.admin-general
//- ----------------------- //- -----------------------
//- URL Handling //- URL Handling
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm.q-mt-md q-card.q-pb-sm.q-mt-md
q-card-section q-card-section
.text-subtitle1 {{t('admin.general.urlHandling')}} .text-subtitle1 {{t('admin.general.urlHandling')}}
q-item q-item
...@@ -247,7 +247,7 @@ q-page.admin-general ...@@ -247,7 +247,7 @@ q-page.admin-general
//- ----------------------- //- -----------------------
//- Logo //- Logo
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm q-card.q-pb-sm
q-card-section q-card-section
.text-subtitle1 {{t('admin.general.logo')}} .text-subtitle1 {{t('admin.general.logo')}}
q-item q-item
...@@ -333,7 +333,7 @@ q-page.admin-general ...@@ -333,7 +333,7 @@ q-page.admin-general
//- ----------------------- //- -----------------------
//- Defaults //- Defaults
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm.q-mt-md(v-if='state.config.defaults') q-card.q-pb-sm.q-mt-md(v-if='state.config.defaults')
q-card-section q-card-section
.text-subtitle1 {{t('admin.general.defaults')}} .text-subtitle1 {{t('admin.general.defaults')}}
q-item q-item
...@@ -409,7 +409,7 @@ q-page.admin-general ...@@ -409,7 +409,7 @@ q-page.admin-general
//- ----------------------- //- -----------------------
//- Uploads //- Uploads
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm.q-mt-md(v-if='state.config.uploads') q-card.q-pb-sm.q-mt-md(v-if='state.config.uploads')
q-card-section q-card-section
.text-subtitle1 {{t('admin.general.uploads')}} .text-subtitle1 {{t('admin.general.uploads')}}
q-item q-item
...@@ -449,7 +449,7 @@ q-page.admin-general ...@@ -449,7 +449,7 @@ q-page.admin-general
//- ----------------------- //- -----------------------
//- SEO //- SEO
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm.q-mt-md(v-if='state.config.robots') q-card.q-pb-sm.q-mt-md(v-if='state.config.robots')
q-card-section q-card-section
.text-subtitle1 SEO .text-subtitle1 SEO
q-item(tag='label') q-item(tag='label')
......
...@@ -40,7 +40,7 @@ q-page.admin-groups ...@@ -40,7 +40,7 @@ q-page.admin-groups
q-separator(inset) q-separator(inset)
.row.q-pa-md.q-col-gutter-md .row.q-pa-md.q-col-gutter-md
.col-12 .col-12
q-card.shadow-1 q-card
q-table( q-table(
:rows='state.groups' :rows='state.groups'
:columns='headers' :columns='headers'
......
...@@ -33,7 +33,7 @@ q-page.admin-icons ...@@ -33,7 +33,7 @@ q-page.admin-icons
q-separator(inset) q-separator(inset)
.row.q-pa-md.q-col-gutter-md .row.q-pa-md.q-col-gutter-md
.col-12 .col-12
q-card.shadow-1 q-card
q-card-section q-card-section
q-card.bg-negative.text-white.rounded-borders(flat) q-card.bg-negative.text-white.rounded-borders(flat)
q-card-section.items-center(horizontal) q-card-section.items-center(horizontal)
......
...@@ -24,7 +24,7 @@ q-page.admin-terminal ...@@ -24,7 +24,7 @@ q-page.admin-terminal
) )
q-separator(inset) q-separator(inset)
.q-pa-md.q-gutter-md .q-pa-md.q-gutter-md
q-card.shadow-1 q-card
q-table( q-table(
:rows='state.instances' :rows='state.instances'
:columns='instancesHeaders' :columns='instancesHeaders'
......
...@@ -45,7 +45,7 @@ q-page.admin-locale ...@@ -45,7 +45,7 @@ q-page.admin-locale
//- ----------------------- //- -----------------------
//- Locale Options //- Locale Options
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm q-card.q-pb-sm
q-card-section q-card-section
.text-subtitle1 {{t('admin.locale.settings')}} .text-subtitle1 {{t('admin.locale.settings')}}
q-item q-item
...@@ -93,7 +93,7 @@ q-page.admin-locale ...@@ -93,7 +93,7 @@ q-page.admin-locale
//- ----------------------- //- -----------------------
//- Namespacing //- Namespacing
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm(v-if='state.namespacing') q-card.q-pb-sm(v-if='state.namespacing')
q-card-section q-card-section
.text-subtitle1 {{t('admin.locale.activeNamespaces')}} .text-subtitle1 {{t('admin.locale.activeNamespaces')}}
......
...@@ -36,7 +36,7 @@ q-page.admin-login ...@@ -36,7 +36,7 @@ q-page.admin-login
//- ----------------------- //- -----------------------
//- Experience //- Experience
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm q-card.q-pb-sm
q-card-section q-card-section
.text-subtitle1 {{t('admin.login.experience')}} .text-subtitle1 {{t('admin.login.experience')}}
q-item q-item
...@@ -137,7 +137,7 @@ q-page.admin-login ...@@ -137,7 +137,7 @@ q-page.admin-login
//- ----------------------- //- -----------------------
//- Providers //- Providers
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm q-card.q-pb-sm
q-card-section q-card-section
.text-subtitle1 {{t('admin.login.providers')}} .text-subtitle1 {{t('admin.login.providers')}}
q-card-section.admin-login-providers.q-pt-none q-card-section.admin-login-providers.q-pt-none
......
...@@ -36,7 +36,7 @@ q-page.admin-mail ...@@ -36,7 +36,7 @@ q-page.admin-mail
//- ----------------------- //- -----------------------
//- Configuration //- Configuration
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm q-card.q-pb-sm
q-card-section q-card-section
.text-subtitle1 {{t('admin.mail.configuration')}} .text-subtitle1 {{t('admin.mail.configuration')}}
q-item q-item
...@@ -68,7 +68,7 @@ q-page.admin-mail ...@@ -68,7 +68,7 @@ q-page.admin-mail
//- ----------------------- //- -----------------------
//- SMTP //- SMTP
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm.q-mt-md q-card.q-pb-sm.q-mt-md
q-card-section q-card-section
.text-subtitle1 {{t('admin.mail.smtp')}} .text-subtitle1 {{t('admin.mail.smtp')}}
q-item q-item
...@@ -168,7 +168,7 @@ q-page.admin-mail ...@@ -168,7 +168,7 @@ q-page.admin-mail
//- ----------------------- //- -----------------------
//- DKIM //- DKIM
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm.q-mt-md q-card.q-pb-sm.q-mt-md
q-card-section q-card-section
.text-subtitle1 {{t('admin.mail.dkim')}} .text-subtitle1 {{t('admin.mail.dkim')}}
q-item.q-pt-none q-item.q-pt-none
...@@ -237,7 +237,7 @@ q-page.admin-mail ...@@ -237,7 +237,7 @@ q-page.admin-mail
//- ----------------------- //- -----------------------
//- MAIL TEMPLATES //- MAIL TEMPLATES
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm q-card.q-pb-sm
q-card-section q-card-section
.text-subtitle1 {{t('admin.mail.templates')}} .text-subtitle1 {{t('admin.mail.templates')}}
q-list q-list
...@@ -271,7 +271,7 @@ q-page.admin-mail ...@@ -271,7 +271,7 @@ q-page.admin-mail
//- ----------------------- //- -----------------------
//- SMTP TEST //- SMTP TEST
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm.q-mt-md q-card.q-pb-sm.q-mt-md
q-card-section q-card-section
.text-subtitle1 {{t('admin.mail.test')}} .text-subtitle1 {{t('admin.mail.test')}}
q-item q-item
......
...@@ -52,7 +52,7 @@ q-page.admin-terminal ...@@ -52,7 +52,7 @@ q-page.admin-terminal
q-card-section.col-auto.q-pr-none q-card-section.col-auto.q-pr-none
q-icon(name='las la-info-circle', size='sm') q-icon(name='las la-info-circle', size='sm')
q-card-section.text-caption {{ t('admin.scheduler.scheduledNone') }} q-card-section.text-caption {{ t('admin.scheduler.scheduledNone') }}
q-card.shadow-1(v-else) q-card(v-else)
q-table( q-table(
:rows='state.scheduledJobs' :rows='state.scheduledJobs'
:columns='scheduledJobsHeaders' :columns='scheduledJobsHeaders'
...@@ -109,7 +109,7 @@ q-page.admin-terminal ...@@ -109,7 +109,7 @@ q-page.admin-terminal
q-card-section.col-auto.q-pr-none q-card-section.col-auto.q-pr-none
q-icon(name='las la-info-circle', size='sm') q-icon(name='las la-info-circle', size='sm')
q-card-section.text-caption {{ t('admin.scheduler.upcomingNone') }} q-card-section.text-caption {{ t('admin.scheduler.upcomingNone') }}
q-card.shadow-1(v-else) q-card(v-else)
q-table( q-table(
:rows='state.upcomingJobs' :rows='state.upcomingJobs'
:columns='upcomingJobsHeaders' :columns='upcomingJobsHeaders'
...@@ -167,7 +167,7 @@ q-page.admin-terminal ...@@ -167,7 +167,7 @@ q-page.admin-terminal
q-card-section.col-auto.q-pr-none q-card-section.col-auto.q-pr-none
q-icon(name='las la-info-circle', size='sm') q-icon(name='las la-info-circle', size='sm')
q-card-section.text-caption {{ t('admin.scheduler.' + state.displayMode + 'None') }} q-card-section.text-caption {{ t('admin.scheduler.' + state.displayMode + 'None') }}
q-card.shadow-1(v-else) q-card(v-else)
q-table( q-table(
:rows='state.jobs' :rows='state.jobs'
:columns='jobsHeaders' :columns='jobsHeaders'
......
...@@ -36,7 +36,7 @@ q-page.admin-mail ...@@ -36,7 +36,7 @@ q-page.admin-mail
//- ----------------------- //- -----------------------
//- Security //- Security
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm q-card.q-pb-sm
q-card-section q-card-section
.text-subtitle1 {{t('admin.security.title')}} .text-subtitle1 {{t('admin.security.title')}}
q-item.q-pt-none q-item.q-pt-none
...@@ -132,7 +132,7 @@ q-page.admin-mail ...@@ -132,7 +132,7 @@ q-page.admin-mail
//- ----------------------- //- -----------------------
//- HSTS //- HSTS
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm.q-mt-md q-card.q-pb-sm.q-mt-md
q-card-section q-card-section
.text-subtitle1 {{t('admin.security.hsts')}} .text-subtitle1 {{t('admin.security.hsts')}}
q-item(tag='label', v-ripple) q-item(tag='label', v-ripple)
...@@ -172,7 +172,7 @@ q-page.admin-mail ...@@ -172,7 +172,7 @@ q-page.admin-mail
//- ----------------------- //- -----------------------
//- Uploads //- Uploads
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm q-card.q-pb-sm
q-card-section q-card-section
.text-subtitle1 {{t('admin.security.uploads')}} .text-subtitle1 {{t('admin.security.uploads')}}
q-item.q-pt-none q-item.q-pt-none
...@@ -226,7 +226,7 @@ q-page.admin-mail ...@@ -226,7 +226,7 @@ q-page.admin-mail
//- ----------------------- //- -----------------------
//- CORS //- CORS
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm.q-mt-md q-card.q-pb-sm.q-mt-md
q-card-section q-card-section
.text-subtitle1 {{t('admin.security.cors')}} .text-subtitle1 {{t('admin.security.cors')}}
q-item q-item
...@@ -279,7 +279,7 @@ q-page.admin-mail ...@@ -279,7 +279,7 @@ q-page.admin-mail
//- ----------------------- //- -----------------------
//- JWT //- JWT
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm.q-mt-md q-card.q-pb-sm.q-mt-md
q-card-section q-card-section
.text-subtitle1 {{t('admin.security.jwt')}} .text-subtitle1 {{t('admin.security.jwt')}}
q-item q-item
......
...@@ -31,7 +31,7 @@ q-page.admin-locale ...@@ -31,7 +31,7 @@ q-page.admin-locale
q-separator(inset) q-separator(inset)
.row.q-pa-md.q-col-gutter-md .row.q-pa-md.q-col-gutter-md
.col-12 .col-12
q-card.shadow-1 q-card
q-list(separator) q-list(separator)
q-item( q-item(
v-for='site of adminStore.sites' v-for='site of adminStore.sites'
......
...@@ -77,7 +77,7 @@ q-page.admin-storage ...@@ -77,7 +77,7 @@ q-page.admin-storage
//- ----------------------- //- -----------------------
//- Setup //- Setup
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm.q-mb-md(v-if='state.target.setup && state.target.setup.handler && state.target.setup.state !== `configured`') q-card.q-pb-sm.q-mb-md(v-if='state.target.setup && state.target.setup.handler && state.target.setup.state !== `configured`')
q-card-section q-card-section
.text-subtitle1 {{t('admin.storage.setup')}} .text-subtitle1 {{t('admin.storage.setup')}}
.text-body2.text-grey {{ t('admin.storage.setupHint') }} .text-body2.text-grey {{ t('admin.storage.setupHint') }}
...@@ -167,7 +167,7 @@ q-page.admin-storage ...@@ -167,7 +167,7 @@ q-page.admin-storage
@click='setupGitHubStep(`verify`)' @click='setupGitHubStep(`verify`)'
:loading='state.setupCfg.loading' :loading='state.setupCfg.loading'
) )
q-card.shadow-1.q-pb-sm.q-mt-md(v-if='state.target.setup && state.target.setup.handler && state.target.setup.state === `configured`') q-card.q-pb-sm.q-mt-md(v-if='state.target.setup && state.target.setup.handler && state.target.setup.state === `configured`')
q-card-section q-card-section
.text-subtitle1 {{t('admin.storage.setup')}} .text-subtitle1 {{t('admin.storage.setup')}}
.text-body2.text-grey {{ t('admin.storage.setupConfiguredHint') }} .text-body2.text-grey {{ t('admin.storage.setupConfiguredHint') }}
...@@ -189,7 +189,7 @@ q-page.admin-storage ...@@ -189,7 +189,7 @@ q-page.admin-storage
//- ----------------------- //- -----------------------
//- Content Types //- Content Types
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm q-card.q-pb-sm
q-card-section q-card-section
.text-subtitle1 {{t('admin.storage.contentTypes')}} .text-subtitle1 {{t('admin.storage.contentTypes')}}
.text-body2.text-grey {{ t('admin.storage.contentTypesHint') }} .text-body2.text-grey {{ t('admin.storage.contentTypesHint') }}
...@@ -262,7 +262,7 @@ q-page.admin-storage ...@@ -262,7 +262,7 @@ q-page.admin-storage
//- ----------------------- //- -----------------------
//- Content Delivery //- Content Delivery
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm.q-mt-md q-card.q-pb-sm.q-mt-md
q-card-section q-card-section
.text-subtitle1 {{t('admin.storage.assetDelivery')}} .text-subtitle1 {{t('admin.storage.assetDelivery')}}
.text-body2.text-grey {{ t('admin.storage.assetDeliveryHint') }} .text-body2.text-grey {{ t('admin.storage.assetDeliveryHint') }}
...@@ -294,7 +294,7 @@ q-page.admin-storage ...@@ -294,7 +294,7 @@ q-page.admin-storage
//- ----------------------- //- -----------------------
//- Configuration //- Configuration
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm.q-mt-md q-card.q-pb-sm.q-mt-md
q-card-section q-card-section
.text-subtitle1 {{t('admin.storage.config')}} .text-subtitle1 {{t('admin.storage.config')}}
q-banner.q-mt-md( q-banner.q-mt-md(
...@@ -367,7 +367,7 @@ q-page.admin-storage ...@@ -367,7 +367,7 @@ q-page.admin-storage
//- ----------------------- //- -----------------------
//- Sync //- Sync
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm.q-mt-md(v-if='state.target.sync && Object.keys(state.target.sync).length > 0') q-card.q-pb-sm.q-mt-md(v-if='state.target.sync && Object.keys(state.target.sync).length > 0')
q-card-section q-card-section
.text-subtitle1 {{t('admin.storage.sync')}} .text-subtitle1 {{t('admin.storage.sync')}}
q-banner.q-mt-md( q-banner.q-mt-md(
...@@ -378,7 +378,7 @@ q-page.admin-storage ...@@ -378,7 +378,7 @@ q-page.admin-storage
//- ----------------------- //- -----------------------
//- Actions //- Actions
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm.q-mt-md q-card.q-pb-sm.q-mt-md
q-card-section q-card-section
.text-subtitle1 {{t('admin.storage.actions')}} .text-subtitle1 {{t('admin.storage.actions')}}
q-banner.q-mt-md( q-banner.q-mt-md(
......
...@@ -37,7 +37,7 @@ q-page.admin-system ...@@ -37,7 +37,7 @@ q-page.admin-system
//- ----------------------- //- -----------------------
//- WIKI.JS //- WIKI.JS
//- ----------------------- //- -----------------------
q-card.q-pb-sm.shadow-1 q-card.q-pb-sm
q-card-section q-card-section
.text-subtitle1 Wiki.js .text-subtitle1 Wiki.js
q-item q-item
...@@ -69,7 +69,7 @@ q-page.admin-system ...@@ -69,7 +69,7 @@ q-page.admin-system
//- CLIENT //- CLIENT
//- ----------------------- //- -----------------------
q-no-ssr q-no-ssr
q-card.q-mt-md.q-pb-sm.shadow-1 q-card.q-mt-md.q-pb-sm
q-card-section q-card-section
.text-subtitle1 {{t('admin.system.client')}} .text-subtitle1 {{t('admin.system.client')}}
q-item q-item
...@@ -116,7 +116,7 @@ q-page.admin-system ...@@ -116,7 +116,7 @@ q-page.admin-system
//- ----------------------- //- -----------------------
//- ENGINES //- ENGINES
//- ----------------------- //- -----------------------
q-card.q-pb-sm.shadow-1 q-card.q-pb-sm
q-card-section q-card-section
.text-subtitle1 {{t('admin.system.engines')}} .text-subtitle1 {{t('admin.system.engines')}}
q-item q-item
...@@ -146,7 +146,7 @@ q-page.admin-system ...@@ -146,7 +146,7 @@ q-page.admin-system
//- ----------------------- //- -----------------------
//- HOST INFORMATION //- HOST INFORMATION
//- ----------------------- //- -----------------------
q-card.q-mt-md.q-pb-sm.shadow-1 q-card.q-mt-md.q-pb-sm
q-card-section q-card-section
.text-subtitle1 {{ t('admin.system.hostInfo') }} .text-subtitle1 {{ t('admin.system.hostInfo') }}
q-item q-item
...@@ -450,6 +450,11 @@ Total RAM: ${state.info.ramTotal}` ...@@ -450,6 +450,11 @@ Total RAM: ${state.info.ramTotal}`
padding: 8px 12px; padding: 8px 12px;
border-radius: 4px; border-radius: 4px;
font-family: 'Roboto Mono', Consolas, "Liberation Mono", Courier, monospace; font-family: 'Roboto Mono', Consolas, "Liberation Mono", Courier, monospace;
@at-root .body--dark & {
background-color: $dark-4;
color: #FFF;
}
} }
} }
</style> </style>
...@@ -43,7 +43,7 @@ q-page.admin-terminal ...@@ -43,7 +43,7 @@ q-page.admin-terminal
) )
q-separator(inset) q-separator(inset)
.q-pa-md.q-gutter-md .q-pa-md.q-gutter-md
q-card.shadow-1 q-card
.admin-terminal-term(ref='termDiv') .admin-terminal-term(ref='termDiv')
</template> </template>
......
...@@ -36,7 +36,7 @@ q-page.admin-theme ...@@ -36,7 +36,7 @@ q-page.admin-theme
//- ----------------------- //- -----------------------
//- Theme Options //- Theme Options
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm q-card.q-pb-sm
q-card-section.flex.items-center q-card-section.flex.items-center
.text-subtitle1 {{t('admin.theme.appearance')}} .text-subtitle1 {{t('admin.theme.appearance')}}
q-space q-space
...@@ -90,7 +90,7 @@ q-page.admin-theme ...@@ -90,7 +90,7 @@ q-page.admin-theme
//- ----------------------- //- -----------------------
//- Theme Layout //- Theme Layout
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm.q-mt-md q-card.q-pb-sm.q-mt-md
q-card-section q-card-section
.text-subtitle1 {{t('admin.theme.layout')}} .text-subtitle1 {{t('admin.theme.layout')}}
q-item q-item
...@@ -170,7 +170,7 @@ q-page.admin-theme ...@@ -170,7 +170,7 @@ q-page.admin-theme
//- ----------------------- //- -----------------------
//- Fonts //- Fonts
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm q-card.q-pb-sm
q-card-section.flex.items-center q-card-section.flex.items-center
.text-subtitle1 {{t('admin.theme.fonts')}} .text-subtitle1 {{t('admin.theme.fonts')}}
q-space q-space
...@@ -216,7 +216,7 @@ q-page.admin-theme ...@@ -216,7 +216,7 @@ q-page.admin-theme
//- ----------------------- //- -----------------------
//- Code Injection //- Code Injection
//- ----------------------- //- -----------------------
q-card.shadow-1.q-pb-sm.q-mt-md q-card.q-pb-sm.q-mt-md
q-card-section q-card-section
.text-subtitle1 {{t('admin.theme.codeInjection')}} .text-subtitle1 {{t('admin.theme.codeInjection')}}
q-item q-item
...@@ -471,12 +471,7 @@ async function save () { ...@@ -471,12 +471,7 @@ async function save () {
siteStore.$patch({ siteStore.$patch({
theme: patchTheme theme: patchTheme
}) })
$q.dark.set(state.config.dark) EVENT_BUS.emit('applyTheme')
setCssVar('primary', state.config.colorPrimary)
setCssVar('secondary', state.config.colorSecondary)
setCssVar('accent', state.config.colorAccent)
setCssVar('header', state.config.colorHeader)
setCssVar('sidebar', state.config.colorSidebar)
} }
$q.notify({ $q.notify({
type: 'positive', type: 'positive',
......
...@@ -41,7 +41,7 @@ q-page.admin-groups ...@@ -41,7 +41,7 @@ q-page.admin-groups
q-separator(inset) q-separator(inset)
.row.q-pa-md.q-col-gutter-md .row.q-pa-md.q-col-gutter-md
.col-12 .col-12
q-card.shadow-1 q-card
q-table( q-table(
:rows='state.users' :rows='state.users'
:columns='headers' :columns='headers'
...@@ -246,7 +246,7 @@ function createUser () { ...@@ -246,7 +246,7 @@ function createUser () {
$q.dialog({ $q.dialog({
component: UserCreateDialog component: UserCreateDialog
}).onOk(() => { }).onOk(() => {
this.load() load()
}) })
} }
......
...@@ -17,7 +17,7 @@ q-page.admin-utilities ...@@ -17,7 +17,7 @@ q-page.admin-utilities
) )
q-separator(inset) q-separator(inset)
.q-pa-md.q-gutter-md .q-pa-md.q-gutter-md
q-card.shadow-1 q-card
q-list(separator) q-list(separator)
q-item q-item
blueprint-icon(icon='disconnected', :hue-rotate='45') blueprint-icon(icon='disconnected', :hue-rotate='45')
......
...@@ -34,7 +34,7 @@ q-page.column ...@@ -34,7 +34,7 @@ q-page.column
v-if='editorStore.isActive' v-if='editorStore.isActive'
) )
component(:is='editorComponents[editorStore.editor]') component(:is='editorComponents[editorStore.editor]')
q-scroll-area( q-scroll-area.page-container-scrl(
v-else v-else
:thumb-style='thumbStyle' :thumb-style='thumbStyle'
:bar-style='barStyle' :bar-style='barStyle'
...@@ -165,6 +165,7 @@ import { useEditorStore } from 'src/stores/editor' ...@@ -165,6 +165,7 @@ import { useEditorStore } from 'src/stores/editor'
import { useFlagsStore } from 'src/stores/flags' import { useFlagsStore } from 'src/stores/flags'
import { usePageStore } from 'src/stores/page' import { usePageStore } from 'src/stores/page'
import { useSiteStore } from 'src/stores/site' import { useSiteStore } from 'src/stores/site'
import { useUserStore } from 'src/stores/user'
// COMPONENTS // COMPONENTS
...@@ -195,6 +196,7 @@ const editorStore = useEditorStore() ...@@ -195,6 +196,7 @@ const editorStore = useEditorStore()
const flagsStore = useFlagsStore() const flagsStore = useFlagsStore()
const pageStore = usePageStore() const pageStore = usePageStore()
const siteStore = useSiteStore() const siteStore = useSiteStore()
const userStore = useUserStore()
// ROUTER // ROUTER
...@@ -269,7 +271,13 @@ watch(() => route.path, async (newValue) => { ...@@ -269,7 +271,13 @@ watch(() => route.path, async (newValue) => {
} catch (err) { } catch (err) {
if (err.message === 'ERR_PAGE_NOT_FOUND') { if (err.message === 'ERR_PAGE_NOT_FOUND') {
if (newValue === '/') { if (newValue === '/') {
siteStore.overlay = 'Welcome' if (!userStore.authenticated) {
router.push('/login')
} else if (!userStore.can('write:pages')) {
router.replace('/_error/unauthorized')
} else {
siteStore.overlay = 'Welcome'
}
} else { } else {
$q.notify({ $q.notify({
type: 'negative', type: 'negative',
...@@ -369,6 +377,10 @@ function refreshTocExpanded (baseToc, lvl) { ...@@ -369,6 +377,10 @@ function refreshTocExpanded (baseToc, lvl) {
// @at-root .body--dark & { // @at-root .body--dark & {
// border-top: 1px solid $dark-6; // border-top: 1px solid $dark-6;
// } // }
.page-container-scrl > .q-scrollarea__container > .q-scrollarea__content {
width: 100%;
}
} }
.page-sidebar { .page-sidebar {
flex: 0 0 300px; flex: 0 0 300px;
......
...@@ -369,6 +369,10 @@ export const usePageStore = defineStore('page', { ...@@ -369,6 +369,10 @@ export const usePageStore = defineStore('page', {
tocDepth: pick(pageData.tocDepth, ['min', 'max']) tocDepth: pick(pageData.tocDepth, ['min', 'max'])
}) })
editorStore.$patch({
mode: 'edit'
})
this.router.replace(`/${this.path}`) this.router.replace(`/${this.path}`)
} else { } else {
const resp = await APOLLO_CLIENT.mutate({ const resp = await APOLLO_CLIENT.mutate({
...@@ -390,32 +394,35 @@ export const usePageStore = defineStore('page', { ...@@ -390,32 +394,35 @@ export const usePageStore = defineStore('page', {
`, `,
variables: { variables: {
id: this.id, id: this.id,
patch: pick(this, [ patch: {
'allowComments', ...pick(this, [
'allowContributions', 'allowComments',
'allowRatings', 'allowContributions',
'content', 'allowRatings',
'description', 'content',
'icon', 'description',
'isBrowsable', 'icon',
'locale', 'isBrowsable',
'password', 'locale',
'path', 'password',
'publishEndDate', 'path',
'publishStartDate', 'publishEndDate',
'publishState', 'publishStartDate',
'relations', 'publishState',
'render', 'relations',
'scriptJsLoad', 'render',
'scriptJsUnload', 'scriptJsLoad',
'scriptCss', 'scriptJsUnload',
'showSidebar', 'scriptCss',
'showTags', 'showSidebar',
'showToc', 'showTags',
'tags', 'showToc',
'title', 'tags',
'tocDepth' 'title',
]) 'tocDepth'
]),
reasonForChange: editorStore.reasonForChange
}
} }
}) })
const result = resp?.data?.updatePage?.operation ?? {} const result = resp?.data?.updatePage?.operation ?? {}
...@@ -427,7 +434,8 @@ export const usePageStore = defineStore('page', { ...@@ -427,7 +434,8 @@ export const usePageStore = defineStore('page', {
const curDate = DateTime.utc() const curDate = DateTime.utc()
editorStore.$patch({ editorStore.$patch({
lastChangeTimestamp: curDate, lastChangeTimestamp: curDate,
lastSaveTimestamp: curDate lastSaveTimestamp: curDate,
reasonForChange: ''
}) })
} catch (err) { } catch (err) {
console.warn(err) console.warn(err)
......
...@@ -5,6 +5,8 @@ import gql from 'graphql-tag' ...@@ -5,6 +5,8 @@ import gql from 'graphql-tag'
import { DateTime } from 'luxon' import { DateTime } from 'luxon'
import { getAccessibleColor } from 'src/helpers/accessibility' import { getAccessibleColor } from 'src/helpers/accessibility'
import { useSiteStore } from './site'
export const useUserStore = defineStore('user', { export const useUserStore = defineStore('user', {
state: () => ({ state: () => ({
id: '10000000-0000-4000-8000-000000000001', id: '10000000-0000-4000-8000-000000000001',
...@@ -18,6 +20,7 @@ export const useUserStore = defineStore('user', { ...@@ -18,6 +20,7 @@ export const useUserStore = defineStore('user', {
appearance: 'site', appearance: 'site',
cvd: 'none', cvd: 'none',
permissions: [], permissions: [],
pagePermissions: [],
iat: 0, iat: 0,
exp: null, exp: null,
authenticated: false, authenticated: false,
...@@ -27,6 +30,9 @@ export const useUserStore = defineStore('user', { ...@@ -27,6 +30,9 @@ export const useUserStore = defineStore('user', {
getters: {}, getters: {},
actions: { actions: {
async refreshAuth () { async refreshAuth () {
if (this.exp && this.exp < DateTime.now()) {
return
}
const jwtCookie = Cookies.get('jwt') const jwtCookie = Cookies.get('jwt')
if (jwtCookie) { if (jwtCookie) {
try { try {
...@@ -38,6 +44,7 @@ export const useUserStore = defineStore('user', { ...@@ -38,6 +44,7 @@ export const useUserStore = defineStore('user', {
this.token = jwtCookie this.token = jwtCookie
if (this.exp <= DateTime.utc()) { if (this.exp <= DateTime.utc()) {
console.info('Token has expired. Attempting renew...') console.info('Token has expired. Attempting renew...')
// TODO: Renew token
} else { } else {
this.authenticated = true this.authenticated = true
} }
...@@ -69,6 +76,7 @@ export const useUserStore = defineStore('user', { ...@@ -69,6 +76,7 @@ export const useUserStore = defineStore('user', {
name name
} }
} }
userPermissions
} }
`, `,
variables: { variables: {
...@@ -90,13 +98,71 @@ export const useUserStore = defineStore('user', { ...@@ -90,13 +98,71 @@ export const useUserStore = defineStore('user', {
this.timeFormat = resp.prefs.timeFormat || '12h' this.timeFormat = resp.prefs.timeFormat || '12h'
this.appearance = resp.prefs.appearance || 'site' this.appearance = resp.prefs.appearance || 'site'
this.cvd = resp.prefs.cvd || 'none' this.cvd = resp.prefs.cvd || 'none'
this.permissions = respRaw.data.userPermissions || []
this.profileLoaded = true this.profileLoaded = true
} catch (err) { } catch (err) {
console.warn(err) console.warn(err)
} }
}, },
logout () {
Cookies.remove('jwt', { path: '/' })
this.$patch({
id: '10000000-0000-4000-8000-000000000001',
email: '',
name: '',
hasAvatar: false,
localeCode: '',
timezone: '',
dateFormat: 'YYYY-MM-DD',
timeFormat: '12h',
appearance: 'site',
cvd: 'none',
permissions: [],
iat: 0,
exp: null,
authenticated: false,
token: '',
profileLoaded: false
})
EVENT_BUS.emit('logout')
},
getAccessibleColor (base, hexBase) { getAccessibleColor (base, hexBase) {
return getAccessibleColor(base, hexBase, this.cvd) return getAccessibleColor(base, hexBase, this.cvd)
},
can (permission) {
if (this.permissions.includes('manage:system') || this.permissions.includes(permission) || this.pagePermissions.includes(permission)) {
return true
}
return false
},
async fetchPagePermissions (path) {
if (path.startsWith('/_')) {
this.pagePermissions = []
return
}
const siteStore = useSiteStore()
try {
const respRaw = await APOLLO_CLIENT.query({
query: gql`
query fetchPagePermissions (
$siteId: UUID!
$path: String!
) {
userPermissionsAtPath(
siteId: $siteId
path: $path
)
}
`,
variables: {
siteId: siteStore.id,
path
}
})
this.pagePermissions = respRaw?.data?.userPermissionsAtPath || []
} catch (err) {
console.warn(`Failed to fetch page permissions at path ${path}!`)
}
} }
} }
}) })
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment