Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wiki-js
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
1
Issues
1
List
Board
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
wiki-js
Commits
960a8a03
You need to sign in or sign up before continuing.
Unverified
Commit
960a8a03
authored
May 01, 2023
by
NGPixel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: update permission system + dark theme fixes + logout
parent
59f6b6fe
Show whitespace changes
Inline
Side-by-side
Showing
45 changed files
with
416 additions
and
195 deletions
+416
-195
kernel.mjs
server/core/kernel.mjs
+1
-1
3.0.0.mjs
server/db/migrations/3.0.0.mjs
+1
-1
user.mjs
server/graph/resolvers/user.mjs
+13
-1
user.graphql
server/graph/schemas/user.graphql
+7
-0
authentication.mjs
server/models/authentication.mjs
+2
-2
users.mjs
server/models/users.mjs
+37
-56
authentication.mjs
server/modules/authentication/local/authentication.mjs
+1
-1
package-lock.json
server/package-lock.json
+0
-0
package.json
server/package.json
+1
-1
App.vue
ux/src/App.vue
+25
-1
AccountMenu.vue
ux/src/components/AccountMenu.vue
+1
-1
GroupEditOverlay.vue
ux/src/components/GroupEditOverlay.vue
+8
-15
HeaderNav.vue
ux/src/components/HeaderNav.vue
+20
-1
PageActionsCol.vue
ux/src/components/PageActionsCol.vue
+24
-13
PageHeader.vue
ux/src/components/PageHeader.vue
+38
-11
app.scss
ux/src/css/app.scss
+25
-0
page-contents.scss
ux/src/css/page-contents.scss
+1
-1
en.json
ux/src/i18n/locales/en.json
+2
-0
AdminLayout.vue
ux/src/layouts/AdminLayout.vue
+36
-11
MainLayout.vue
ux/src/layouts/MainLayout.vue
+8
-7
ProfileLayout.vue
ux/src/layouts/ProfileLayout.vue
+1
-1
AdminAuth.vue
ux/src/pages/AdminAuth.vue
+2
-2
AdminDashboard.vue
ux/src/pages/AdminDashboard.vue
+14
-6
AdminEditors.vue
ux/src/pages/AdminEditors.vue
+1
-1
AdminExtensions.vue
ux/src/pages/AdminExtensions.vue
+1
-1
AdminFlags.vue
ux/src/pages/AdminFlags.vue
+2
-2
AdminGeneral.vue
ux/src/pages/AdminGeneral.vue
+8
-8
AdminGroups.vue
ux/src/pages/AdminGroups.vue
+1
-1
AdminIcons.vue
ux/src/pages/AdminIcons.vue
+1
-1
AdminInstances.vue
ux/src/pages/AdminInstances.vue
+1
-1
AdminLocale.vue
ux/src/pages/AdminLocale.vue
+2
-2
AdminLogin.vue
ux/src/pages/AdminLogin.vue
+2
-2
AdminMail.vue
ux/src/pages/AdminMail.vue
+5
-5
AdminScheduler.vue
ux/src/pages/AdminScheduler.vue
+3
-3
AdminSecurity.vue
ux/src/pages/AdminSecurity.vue
+5
-5
AdminSites.vue
ux/src/pages/AdminSites.vue
+1
-1
AdminStorage.vue
ux/src/pages/AdminStorage.vue
+7
-7
AdminSystem.vue
ux/src/pages/AdminSystem.vue
+9
-4
AdminTerminal.vue
ux/src/pages/AdminTerminal.vue
+1
-1
AdminTheme.vue
ux/src/pages/AdminTheme.vue
+5
-10
AdminUsers.vue
ux/src/pages/AdminUsers.vue
+2
-2
AdminUtilities.vue
ux/src/pages/AdminUtilities.vue
+1
-1
Index.vue
ux/src/pages/Index.vue
+13
-1
page.js
ux/src/stores/page.js
+11
-3
user.js
ux/src/stores/user.js
+66
-0
No files found.
server/core/kernel.mjs
View file @
960a8a03
...
...
@@ -39,7 +39,7 @@ export default {
*/
async
preBootWeb
()
{
try
{
WIKI
.
cache
=
new
NodeCache
()
WIKI
.
cache
=
new
NodeCache
(
{
checkperiod
:
0
}
)
WIKI
.
scheduler
=
await
scheduler
.
init
()
WIKI
.
servers
=
servers
WIKI
.
events
=
{
...
...
server/db/migrations/3.0.0.mjs
View file @
960a8a03
import
{
v4
as
uuid
}
from
'uuid'
import
bcrypt
from
'bcryptjs
-then
'
import
bcrypt
from
'bcryptjs'
import
crypto
from
'node:crypto'
import
{
DateTime
}
from
'luxon'
import
{
pem2jwk
}
from
'pem-jwk'
...
...
server/graph/resolvers/user.mjs
View file @
960a8a03
...
...
@@ -85,12 +85,24 @@ export default {
.
whereNotNull
(
'lastLoginAt'
)
.
orderBy
(
'lastLoginAt'
,
'desc'
)
.
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
:
{
async
createUser
(
obj
,
args
)
{
try
{
await
WIKI
.
db
.
users
.
createNewUser
({
...
args
,
passwordRaw
:
args
.
password
,
isVerified
:
true
})
await
WIKI
.
db
.
users
.
createNewUser
({
...
args
,
isVerified
:
true
})
return
{
operation
:
generateSuccess
(
'User created successfully'
)
...
...
server/graph/schemas/user.graphql
View file @
960a8a03
...
...
@@ -17,6 +17,13 @@ extend type Query {
):
User
lastLogins
:
[
UserLastLogin
]
userPermissions
:
[
String
]
userPermissionsAtPath
(
siteId
:
UUID
!
path
:
String
!
):
[
String
]
}
extend
type
Mutation
{
...
...
server/models/authentication.mjs
View file @
960a8a03
...
...
@@ -29,8 +29,8 @@ export class Authentication extends Model {
return
[
'config'
,
'domainWhitelist'
,
'autoEnrollGroups'
]
}
static
async
getStrategy
(
key
)
{
return
WIKI
.
db
.
authentication
.
query
().
findOne
({
key
})
static
async
getStrategy
(
module
)
{
return
WIKI
.
db
.
authentication
.
query
().
findOne
({
module
})
}
static
async
getStrategies
({
enabledOnly
=
false
}
=
{})
{
...
...
server/models/users.mjs
View file @
960a8a03
...
...
@@ -6,12 +6,11 @@ import jwt from 'jsonwebtoken'
import
{
Model
}
from
'objection'
import
validate
from
'validate.js'
import
qr
from
'qr-image'
import
bcrypt
from
'bcryptjs'
import
{
Group
}
from
'./groups.mjs'
import
{
Locale
}
from
'./locales.mjs'
const
bcryptRegexp
=
/^
\$
2
[
ayb
]\$[
0-9
]{2}\$[
A-Za-z0-9.
/]{53}
$/
/**
* Users model
*/
...
...
@@ -70,18 +69,12 @@ export class User extends Model {
await
super
.
$beforeUpdate
(
opt
,
context
)
this
.
updatedAt
=
new
Date
().
toISOString
()
if
(
!
(
opt
.
patch
&&
this
.
password
===
undefined
))
{
await
this
.
generateHash
()
}
}
async
$beforeInsert
(
context
)
{
await
super
.
$beforeInsert
(
context
)
this
.
createdAt
=
new
Date
().
toISOString
()
this
.
updatedAt
=
new
Date
().
toISOString
()
await
this
.
generateHash
()
}
// ------------------------------------------------
...
...
@@ -524,16 +517,14 @@ export class User extends Model {
*
* @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
email
=
email
.
toLowerCase
()
email
=
email
.
toLowerCase
()
.
trim
()
// Input validation
let
validation
=
null
if
(
providerKey
===
'local'
)
{
validation
=
validate
({
const
validation
=
validate
({
email
,
passwordRaw
,
password
,
name
},
{
email
:
{
...
...
@@ -542,7 +533,7 @@ export class User extends Model {
maximum
:
255
}
},
passwordRaw
:
{
password
:
{
presence
:
{
allowEmpty
:
false
},
...
...
@@ -560,56 +551,49 @@ export class User extends Model {
}
}
},
{
format
:
'flat'
})
}
else
{
validation
=
validate
({
email
,
name
},
{
email
:
{
email
:
true
,
length
:
{
maximum
:
255
}
},
name
:
{
presence
:
{
allowEmpty
:
false
},
length
:
{
minimum
:
2
,
maximum
:
255
}
}
},
{
format
:
'flat'
})
}
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
const
usr
=
await
WIKI
.
db
.
users
.
query
().
findOne
({
email
,
providerKey
})
if
(
!
usr
)
{
const
usr
=
await
WIKI
.
db
.
users
.
query
().
findOne
({
email
})
if
(
usr
)
{
throw
new
Error
(
'ERR_ACCOUNT_ALREADY_EXIST'
)
}
// Create the account
let
newUsrData
=
{
providerKey
,
const
localAuth
=
await
WIKI
.
db
.
authentication
.
getStrategy
(
'local'
)
const
newUsr
=
await
WIKI
.
db
.
users
.
query
().
insert
({
email
,
name
,
locale
:
'en'
,
defaultEditor
:
'markdown'
,
tfaIsActive
:
false
,
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
,
mustChangePwd
:
false
}
if
(
providerKey
===
`local`
)
{
newUsrData
.
password
=
passwordRaw
newUsrData
.
mustChangePwd
=
(
mustChangePassword
===
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
)
{
...
...
@@ -632,9 +616,6 @@ export class User extends Model {
text
:
`You've been invited to the wiki
${
WIKI
.
config
.
title
}
:
${
WIKI
.
config
.
host
}
/login`
})
}
}
else
{
throw
new
WIKI
.
Error
.
AuthAccountAlreadyExists
()
}
}
/**
...
...
server/modules/authentication/local/authentication.mjs
View file @
960a8a03
/* global WIKI */
import
bcrypt
from
'bcryptjs
-then
'
import
bcrypt
from
'bcryptjs'
// ------------------------------------
// Local Account
...
...
server/package-lock.json
View file @
960a8a03
B
{
...
...
server/package.json
View file @
960a8a03
...
...
@@ -49,7 +49,7 @@
"apollo-server-express"
:
"3.6.7"
,
"auto-load"
:
"3.0.4"
,
"aws-sdk"
:
"2.1353.0"
,
"bcryptjs
-then"
:
"1.0.1
"
,
"bcryptjs
"
:
"2.4.3
"
,
"body-parser"
:
"1.20.2"
,
"chalk"
:
"5.2.0"
,
"cheerio"
:
"1.0.0-rc.12"
,
...
...
ux/src/App.vue
View file @
960a8a03
...
...
@@ -9,6 +9,7 @@ import { useFlagsStore } from 'src/stores/flags'
import
{
useSiteStore
}
from
'src/stores/site'
import
{
useUserStore
}
from
'src/stores/user'
import
{
setCssVar
,
useQuasar
}
from
'quasar'
import
{
useI18n
}
from
'vue-i18n'
import
'@mdi/font/css/materialdesignicons.css'
...
...
@@ -24,6 +25,10 @@ const flagsStore = useFlagsStore()
const
siteStore
=
useSiteStore
()
const
userStore
=
useUserStore
()
// I18N
const
{
t
}
=
useI18n
()
// ROUTER
const
router
=
useRouter
()
...
...
@@ -94,14 +99,33 @@ router.beforeEach(async (to, from) => {
console
.
info
(
`Refreshing user
${
userStore
.
id
}
profile...`
)
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
()
})
// LOADER
router
.
afterEach
(()
=>
{
if
(
!
state
.
isInitialized
)
{
state
.
isInitialized
=
true
applyTheme
()
document
.
querySelector
(
'.init-loading'
).
remove
()
}
siteStore
.
routerLoading
=
false
})
</
script
>
ux/src/components/AccountMenu.vue
View file @
960a8a03
...
...
@@ -28,7 +28,7 @@ q-btn.q-ml-md(flat, round, dense, color='grey')
:label='t(`common.header.logout`)'
icon='las la-sign-out-alt'
color='red'
href='/logout
'
@click='userStore.logout()
'
no-caps
)
q-tooltip
{{
t
(
'common.header.account'
)
}}
...
...
ux/src/components/GroupEditOverlay.vue
View file @
960a8a03
...
...
@@ -584,50 +584,43 @@ const usersHeaders = [
const
permissions
=
[
{
permission
:
'
write:users
'
,
hint
:
'Can
create or authorize new users, but not modify existing ones
'
,
permission
:
'
access:admin
'
,
hint
:
'Can
access the administration area.
'
,
warning
:
false
,
restrictedForSystem
:
true
,
disabled
:
false
},
{
permission
:
'manage:users'
,
hint
:
'Can manage all 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'
,
hint
:
'Can create / manage users (but not users with administrative permissions)'
,
warning
:
false
,
restrictedForSystem
:
true
,
disabled
:
false
},
{
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
,
restrictedForSystem
:
true
,
disabled
:
false
},
{
permission
:
'manage:navigation'
,
hint
:
'Can manage
the
site navigation'
,
hint
:
'Can manage site navigation'
,
warning
:
false
,
restrictedForSystem
:
true
,
disabled
:
false
},
{
permission
:
'manage:theme'
,
hint
:
'Can m
anage and modify theme
s'
,
hint
:
'Can m
odify site theme setting
s'
,
warning
:
false
,
restrictedForSystem
:
true
,
disabled
:
false
},
{
permission
:
'manage:
api
'
,
hint
:
'Can
generate and revoke API key
s'
,
permission
:
'manage:
sites
'
,
hint
:
'Can
create / manage site
s'
,
warning
:
true
,
restrictedForSystem
:
true
,
disabled
:
false
...
...
ux/src/components/HeaderNav.vue
View file @
960a8a03
...
...
@@ -73,6 +73,7 @@ q-header.bg-header.text-white.site-header(
size='24px'
)
q-btn.q-ml-md(
v-if='userStore.can(`write:pages`)'
flat
round
dense
...
...
@@ -83,6 +84,7 @@ q-header.bg-header.text-white.site-header(
q-tooltip Create New Page
new-menu
q-btn.q-ml-md(
v-if='userStore.can(`browse:fileman`)'
flat
round
dense
...
...
@@ -93,6 +95,7 @@ q-header.bg-header.text-white.site-header(
)
q-tooltip File Manager
q-btn.q-ml-md(
v-if='userStore.can(`access:admin`)'
flat
round
dense
...
...
@@ -102,7 +105,21 @@ q-header.bg-header.text-white.site-header(
:aria-label='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
>
<
script
setup
>
...
...
@@ -114,6 +131,7 @@ import { useQuasar } from 'quasar'
import
{
reactive
}
from
'vue'
import
{
useSiteStore
}
from
'src/stores/site'
import
{
useUserStore
}
from
'src/stores/user'
// QUASAR
...
...
@@ -122,6 +140,7 @@ const $q = useQuasar()
// STORES
const
siteStore
=
useSiteStore
()
const
userStore
=
useUserStore
()
// I18N
...
...
ux/src/components/PageActionsCol.vue
View file @
960a8a03
<
template
lang=
"pug"
>
.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'
...
...
@@ -9,15 +10,30 @@
)
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
v-if='flagsStore.experimental'
)
q-tooltip(anchor='center left' self='center right') Page Data
q-separator.q-my-sm(inset)
q-btn.q-py-md(
flat
icon='las la-history'
:color='editorStore.isActive ? `white` : `grey`'
aria-label='Page History'
)
q-tooltip(anchor='center left' self='center right') Page History
q-btn.q-py-md(
flat
icon='las la-code'
:color='editorStore.isActive ? `white` : `grey`'
aria-label='Page Source'
)
q-tooltip(anchor='center left' self='center right') Page Source
template(v-if='!(editorStore.isActive && editorStore.mode === `create`)')
q-separator.q-my-sm(inset)
q-btn.q-py-sm(
...
...
@@ -34,22 +50,12 @@
transition-show='jump-left'
)
q-list(padding, style='min-width: 225px;')
q-item(clickable)
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(clickable, v-if='userStore.can(`manage:pages`)')
q-item-section.items-center(avatar)
q-icon(color='deep-orange-9', name='las la-atom', size='sm')
q-item-section
q-item-label Convert Page
q-item(clickable)
q-item(clickable
, v-if='userStore.can(`edit:pages`)'
)
q-item-section.items-center(avatar)
q-icon(color='deep-orange-9', name='las la-magic', size='sm')
q-item-section
...
...
@@ -62,6 +68,7 @@
q-space
template(v-if='!(editorStore.isActive && editorStore.mode === `create`)')
q-btn.q-py-sm(
v-if='userStore.can(`create:pages`)'
flat
icon='las la-copy'
:color='editorStore.isActive ? `deep-orange-2` : `grey`'
...
...
@@ -70,6 +77,7 @@
)
q-tooltip(anchor='center left' self='center right') Duplicate Page
q-btn.q-py-sm(
v-if='userStore.can(`manage:pages`)'
flat
icon='las la-share'
:color='editorStore.isActive ? `deep-orange-2` : `grey`'
...
...
@@ -78,6 +86,7 @@
)
q-tooltip(anchor='center left' self='center right') Rename / Move Page
q-btn.q-py-sm(
v-if='userStore.can(`delete:pages`)'
flat
icon='las la-trash'
:color='editorStore.isActive ? `deep-orange-2` : `grey`'
...
...
@@ -99,6 +108,7 @@ import { useEditorStore } from 'src/stores/editor'
import
{
useFlagsStore
}
from
'src/stores/flags'
import
{
usePageStore
}
from
'src/stores/page'
import
{
useSiteStore
}
from
'src/stores/site'
import
{
useUserStore
}
from
'src/stores/user'
// QUASAR
...
...
@@ -110,6 +120,7 @@ const editorStore = useEditorStore()
const
flagsStore
=
useFlagsStore
()
const
pageStore
=
usePageStore
()
const
siteStore
=
useSiteStore
()
const
userStore
=
useUserStore
()
// ROUTER
...
...
ux/src/components/PageHeader.vue
View file @
960a8a03
...
...
@@ -73,7 +73,8 @@
//- PAGE ACTIONS
.col-auto.q-pa-md.flex.items-center.justify-end
template(v-if='!editorStore.isActive')
q-btn.q-mr-md(
q-btn.q-ml-md(
v-if='userStore.authenticated'
flat
dense
icon='las la-bell'
...
...
@@ -81,7 +82,8 @@
aria-label='Watch Page'
)
q-tooltip Watch Page
q-btn.q-mr-md(
q-btn.q-ml-md(
v-if='userStore.authenticated'
flat
dense
icon='las la-bookmark'
...
...
@@ -89,7 +91,7 @@
aria-label='Bookmark Page'
)
q-tooltip Bookmark Page
q-btn.q-m
r
-md(
q-btn.q-m
l
-md(
flat
dense
icon='las la-share-alt'
...
...
@@ -98,7 +100,7 @@
)
q-tooltip Share
social-sharing-menu
q-btn.q-m
r
-md(
q-btn.q-m
l
-md(
flat
dense
icon='las la-print'
...
...
@@ -107,7 +109,7 @@
)
q-tooltip Print
template(v-if='editorStore.isActive')
q-btn.q-m
r-sm
.acrylic-btn(
q-btn.q-m
l-md
.acrylic-btn(
icon='las la-question-circle'
flat
color='grey'
...
...
@@ -115,7 +117,7 @@
target='_blank'
type='a'
)
q-btn.q-m
r
-sm.acrylic-btn(
q-btn.q-m
l
-sm.acrylic-btn(
icon='las la-cog'
flat
color='grey'
...
...
@@ -123,7 +125,7 @@
@click='openEditorSettings'
)
template(v-if='editorStore.isActive || editorStore.hasPendingChanges')
q-btn.acrylic-btn.q-m
r
-sm(
q-btn.acrylic-btn.q-m
l
-sm(
flat
icon='las la-times'
color='negative'
...
...
@@ -132,7 +134,7 @@
no-caps
@click='discardChanges'
)
q-btn.acrylic-btn(
q-btn.acrylic-btn
.q-ml-sm
(
v-if='editorStore.mode === `create`'
flat
icon='las la-check'
...
...
@@ -142,7 +144,7 @@
no-caps
@click='createPage'
)
q-btn.acrylic-btn(
q-btn.acrylic-btn
.q-ml-sm
(
v-else
flat
icon='las la-check'
...
...
@@ -153,8 +155,8 @@
no-caps
@click='saveChanges'
)
template(v-else)
q-btn.acrylic-btn(
template(v-else
-if='userStore.can(`edit:pages`)'
)
q-btn.acrylic-btn
.q-ml-md
(
flat
icon='las la-edit'
color='deep-orange-9'
...
...
@@ -292,6 +294,31 @@ async function saveChangesCommit () {
}
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
({
component
:
defineAsyncComponent
(()
=>
import
(
'../components/TreeBrowserDialog.vue'
)),
componentProps
:
{
...
...
ux/src/css/app.scss
View file @
960a8a03
...
...
@@ -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
// ------------------------------------------------------------------
...
...
@@ -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
// ------------------------------------------------------------------
...
...
@@ -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
// ------------------------------------------------------------------
...
...
ux/src/css/page-contents.scss
View file @
960a8a03
...
...
@@ -133,7 +133,7 @@
&
:
:
before
{
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
;
margin-top
:
-12px
;
top
:
50%
;
...
...
ux/src/i18n/locales/en.json
View file @
960a8a03
...
...
@@ -1129,6 +1129,7 @@
"auth.loginRequired"
:
"Login required"
,
"auth.loginSuccess"
:
"Login Successful! Redirecting..."
,
"auth.loginUsingStrategy"
:
"Login using {strategy}"
,
"auth.logoutSuccess"
:
"You've been logged out successfully."
,
"auth.missingEmail"
:
"Missing email address."
,
"auth.missingName"
:
"Name is missing."
,
"auth.missingPassword"
:
"Missing password."
,
...
...
@@ -1179,6 +1180,7 @@
"common.actions.generate"
:
"Generate"
,
"common.actions.howItWorks"
:
"How it works"
,
"common.actions.insert"
:
"Insert"
,
"common.actions.login"
:
"Login"
,
"common.actions.manage"
:
"Manage"
,
"common.actions.move"
:
"Move"
,
"common.actions.moveTo"
:
"Move To"
,
...
...
ux/src/layouts/AdminLayout.vue
View file @
960a8a03
...
...
@@ -46,12 +46,13 @@ q-layout.admin(view='hHh Lpr lff')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-apps-tab.svg')
q-item-section
{{
t
(
'admin.dashboard.title'
)
}}
q-item(to='/_admin/sites', v-ripple, active-class='bg-primary text-white')
q-item(to='/_admin/sites', v-ripple, active-class='bg-primary text-white'
, v-if='userStore.can(`manage:sites`)'
)
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-change-theme.svg')
q-item-section
{{
t
(
'admin.sites.title'
)
}}
q-item-section(side)
q-badge(color='dark-3', :label='adminStore.sites.length')
template(v-if='siteSectionShown')
q-item-label.q-mt-sm(header).text-caption.text-blue-grey-4
{{
t
(
'admin.nav.site'
)
}}
q-item.q-mb-md
q-item-section
...
...
@@ -87,47 +88,49 @@ q-layout.admin(view='hHh Lpr lff')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-rfid-tag.svg')
q-item-section
{{
t
(
'admin.blocks.title'
)
}}
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/editors`', v-ripple, active-class='bg-primary text-white
')
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/editors`', v-ripple, active-class='bg-primary text-white', v-if='userStore.can(`manage:sites`)
')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-cashbook.svg')
q-item-section
{{
t
(
'admin.editors.title'
)
}}
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/locale`', v-ripple, active-class='bg-primary text-white
')
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/locale`', v-ripple, active-class='bg-primary text-white', v-if='userStore.can(`manage:sites`)
')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-language.svg')
q-item-section
{{
t
(
'admin.locale.title'
)
}}
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/login`', v-ripple, active-class='bg-primary text-white
')
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/login`', v-ripple, active-class='bg-primary text-white', v-if='userStore.can(`manage:sites`)
')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-bunch-of-keys.svg')
q-item-section
{{
t
(
'admin.login.title'
)
}}
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/navigation`', v-ripple, active-class='bg-primary text-white
')
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/navigation`', v-ripple, active-class='bg-primary text-white', v-if='userStore.can(`manage:sites`) || userStore.can(`manage:navigation`)
')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-tree-structure.svg')
q-item-section
{{
t
(
'admin.navigation.title'
)
}}
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/storage`', v-ripple, active-class='bg-primary text-white
')
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/storage`', v-ripple, active-class='bg-primary text-white', v-if='userStore.can(`manage:sites`)
')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-ssd.svg')
q-item-section
{{
t
(
'admin.storage.title'
)
}}
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/theme`', v-ripple, active-class='bg-primary text-white
')
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/theme`', v-ripple, active-class='bg-primary text-white', v-if='userStore.can(`manage:sites`) || userStore.can(`manage:theme`)
')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-paint-roller.svg')
q-item-section
{{
t
(
'admin.theme.title'
)
}}
template(v-if='usersSectionShown')
q-item-label.q-mt-sm(header).text-caption.text-blue-grey-4
{{
t
(
'admin.nav.users'
)
}}
q-item(to='/_admin/auth', v-ripple, active-class='bg-primary text-white
')
q-item(to='/_admin/auth', v-ripple, active-class='bg-primary text-white', v-if='userStore.can(`manage:system`)
')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-security-lock.svg')
q-item-section
{{
t
(
'admin.auth.title'
)
}}
q-item(to='/_admin/groups', v-ripple, active-class='bg-primary text-white
')
q-item(to='/_admin/groups', v-ripple, active-class='bg-primary text-white', v-if='userStore.can(`manage:groups`)
')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-people.svg')
q-item-section
{{
t
(
'admin.groups.title'
)
}}
q-item-section(side)
q-badge(color='dark-3', :label='adminStore.info.groupsTotal')
q-item(to='/_admin/users', v-ripple, active-class='bg-primary text-white
')
q-item(to='/_admin/users', v-ripple, active-class='bg-primary text-white', v-if='userStore.can(`manage:users`)
')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-account.svg')
q-item-section
{{
t
(
'admin.users.title'
)
}}
q-item-section(side)
q-badge(color='dark-3', :label='adminStore.info.usersTotal')
template(v-if='userStore.can(`manage:system`)')
q-item-label.q-mt-sm(header).text-caption.text-blue-grey-4
{{
t
(
'admin.nav.system'
)
}}
q-item(to='/_admin/api', v-ripple, active-class='bg-primary text-white')
q-item-section(avatar)
...
...
@@ -215,13 +218,14 @@ q-layout.admin(view='hHh Lpr lff')
<
script
setup
>
import
{
useMeta
,
useQuasar
,
setCssVar
}
from
'quasar'
import
{
defineAsyncComponent
,
onMounted
,
reactive
,
ref
,
watch
}
from
'vue'
import
{
computed
,
defineAsyncComponent
,
onMounted
,
reactive
,
ref
,
watch
}
from
'vue'
import
{
useRouter
,
useRoute
}
from
'vue-router'
import
{
useI18n
}
from
'vue-i18n'
import
{
useAdminStore
}
from
'src/stores/admin'
import
{
useFlagsStore
}
from
'src/stores/flags'
import
{
useSiteStore
}
from
'src/stores/site'
import
{
useUserStore
}
from
'src/stores/user'
// COMPONENTS
...
...
@@ -242,6 +246,7 @@ const $q = useQuasar()
const
adminStore
=
useAdminStore
()
const
flagsStore
=
useFlagsStore
()
const
siteStore
=
useSiteStore
()
const
userStore
=
useUserStore
()
// ROUTER
...
...
@@ -279,8 +284,23 @@ const barStyle = {
width
:
'7px'
}
// COMPUTED
const
siteSectionShown
=
computed
(()
=>
{
return
userStore
.
can
(
'manage:sites'
)
||
userStore
.
can
(
'manage:navigation'
)
||
userStore
.
can
(
'manage:theme'
)
})
const
usersSectionShown
=
computed
(()
=>
{
return
userStore
.
can
(
'manage:groups'
)
||
userStore
.
can
(
'manage:users'
)
})
// WATCHERS
watch
(()
=>
route
.
path
,
async
(
newValue
)
=>
{
if
(
!
newValue
.
startsWith
(
'/_admin'
))
{
return
}
if
(
!
userStore
.
can
(
'access:admin'
))
{
router
.
replace
(
'/_error/unauthorized'
)
}
},
{
immediate
:
true
})
watch
(()
=>
adminStore
.
sites
,
(
newValue
)
=>
{
if
(
adminStore
.
currentSiteId
===
null
&&
newValue
.
length
>
0
)
{
adminStore
.
$patch
({
...
...
@@ -300,6 +320,11 @@ watch(() => adminStore.currentSiteId, (newValue) => {
// MOUNTED
onMounted
(
async
()
=>
{
if
(
!
userStore
.
can
(
'access:admin'
))
{
router
.
replace
(
'/_error/unauthorized'
)
return
}
await
adminStore
.
fetchSites
()
if
(
route
.
params
.
siteid
)
{
adminStore
.
$patch
({
...
...
ux/src/layouts/MainLayout.vue
View file @
960a8a03
...
...
@@ -27,7 +27,6 @@ q-layout(view='hHh Lpr lff')
label='Browse'
aria-label='Browse'
size='sm'
@click='openFileManager'
)
q-scroll-area.sidebar-nav(
:thumb-style='thumbStyle'
...
...
@@ -38,21 +37,21 @@ q-layout(view='hHh Lpr lff')
dense
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-section(side)
q-icon(name='las la-dog', color='white')
q-item-section
Requirements
q-item-section
Link 1
q-item(to='/install')
q-item-section(side)
q-icon(name='las la-cat', color='white')
q-item-section
Installation
q-item-section
Link 2
q-separator.q-my-sm(dark)
q-item(to='/install')
q-item-section(side)
q-icon(name='
las la-cat
', color='white')
q-item-section
Installation
q-bar.bg-blue-9.text-white(dense, v-if='flagsStore.experimental')
q-icon(name='
mdi-fruit-grapes
', color='white')
q-item-section
Link 3
q-bar.bg-blue-9.text-white(dense, v-if='flagsStore.experimental
&& userStore.authenticated
')
q-btn.col(
icon='las la-dharmachakra'
label='History'
...
...
@@ -90,6 +89,7 @@ import { useI18n } from 'vue-i18n'
import
{
useEditorStore
}
from
'src/stores/editor'
import
{
useFlagsStore
}
from
'src/stores/flags'
import
{
useSiteStore
}
from
'src/stores/site'
import
{
useUserStore
}
from
'src/stores/user'
// COMPONENTS
...
...
@@ -106,6 +106,7 @@ const $q = useQuasar()
const
editorStore
=
useEditorStore
()
const
flagsStore
=
useFlagsStore
()
const
siteStore
=
useSiteStore
()
const
userStore
=
useUserStore
()
// ROUTER
...
...
ux/src/layouts/ProfileLayout.vue
View file @
960a8a03
...
...
@@ -32,7 +32,7 @@ q-layout(view='hHh Lpr lff')
q-item(
clickable
v-ripple
href='/logout
'
@click='userStore.logout()
'
)
q-item-section(side)
q-icon(name='las la-sign-out-alt', color='negative')
...
...
ux/src/pages/AdminAuth.vue
View file @
960a8a03
...
...
@@ -73,7 +73,7 @@ q-page.admin-mail
q-item-label: strong
{{
str
.
title
}}
q-item-label(caption, lines='2')
{{
str
.
description
}}
.col
q-card.
shadow-1.
q-pb-sm
q-card.q-pb-sm
q-card-section
.text-subtitle1
{{
t
(
'admin.storage.contentTypes'
)
}}
.text-body2.text-grey
{{
t
(
'admin.storage.contentTypesHint'
)
}}
...
...
@@ -81,7 +81,7 @@ q-page.admin-mail
//- -----------------------
//- Configuration
//- -----------------------
q-card.
shadow-1.
q-pb-sm.q-mt-md
q-card.q-pb-sm.q-mt-md
q-card-section
.text-subtitle1
{{
t
(
'admin.storage.config'
)
}}
q-banner.q-mt-md(
...
...
ux/src/pages/AdminDashboard.vue
View file @
960a8a03
...
...
@@ -8,7 +8,7 @@ q-page.admin-dashboard
.text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s
{{
t
(
'admin.dashboard.subtitle'
)
}}
.row.q-px-md.q-col-gutter-md
.col-12.col-sm-6.col-lg-3
q-card
.shadow-1
q-card
q-card-section.admin-dashboard-card
img(src='/_assets/icons/fluent-change-theme.svg')
div
...
...
@@ -21,6 +21,7 @@ q-page.admin-dashboard
color='primary'
icon='las la-plus-circle'
:label='t(`common.actions.new`)'
:disable='!userStore.can(`manage:sites`)'
@click='newSite'
)
q-separator.q-mx-sm(vertical)
...
...
@@ -29,10 +30,11 @@ q-page.admin-dashboard
color='primary'
icon='las la-sitemap'
:label='t(`common.actions.manage`)'
:disable='!userStore.can(`manage:sites`)'
to='/_admin/sites'
)
.col-12.col-sm-6.col-lg-3
q-card
.shadow-1
q-card
q-card-section.admin-dashboard-card
img(src='/_assets/icons/fluent-account.svg')
div
...
...
@@ -45,6 +47,7 @@ q-page.admin-dashboard
color='primary'
icon='las la-user-plus'
:label='t(`common.actions.new`)'
:disable='!userStore.can(`manage:users`)'
@click='newUser'
)
q-separator.q-mx-sm(vertical)
...
...
@@ -53,10 +56,11 @@ q-page.admin-dashboard
color='primary'
icon='las la-users'
:label='t(`common.actions.manage`)'
:disable='!userStore.can(`manage:users`)'
to='/_admin/users'
)
.col-12.col-sm-6.col-lg-3
q-card
.shadow-1
q-card
q-card-section.admin-dashboard-card
img(src='/_assets/icons/fluent-female-working-with-a-laptop.svg')
div
...
...
@@ -69,10 +73,11 @@ q-page.admin-dashboard
color='primary'
icon='las la-chart-area'
:label='t(`admin.analytics.title`)'
:disable='!userStore.can(`manage:sites`)'
:to='`/_admin/` + adminStore.currentSiteId + `/analytics`'
)
.col-12.col-sm-6.col-lg-3
q-card
.shadow-1
q-card
q-card-section.admin-dashboard-card
img(src='/_assets/icons/fluent-ssd-animated.svg')
div
...
...
@@ -85,6 +90,7 @@ q-page.admin-dashboard
color='primary'
icon='las la-server'
:label='t(`common.actions.manage`)'
:disable='!userStore.can(`manage:sites`)'
:to='`/_admin/` + adminStore.currentSiteId + `/storage`'
)
.col-12
...
...
@@ -96,7 +102,7 @@ q-page.admin-dashboard
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-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(
flat
:label='t(`admin.system.checkForUpdates`)'
...
...
@@ -109,7 +115,7 @@ q-page.admin-dashboard
to='/_admin/system'
)
.col-12
q-card
.shadow-1
q-card
q-card-section ---
//- v-container(fluid, grid-list-lg)
...
...
@@ -224,6 +230,7 @@ import { useI18n } from 'vue-i18n'
import
{
useRouter
}
from
'vue-router'
import
{
useAdminStore
}
from
'../stores/admin'
import
{
useUserStore
}
from
'src/stores/user'
// COMPONENTS
...
...
@@ -238,6 +245,7 @@ const $q = useQuasar()
// STORES
const
adminStore
=
useAdminStore
()
const
userStore
=
useUserStore
()
// ROUTER
...
...
ux/src/pages/AdminEditors.vue
View file @
960a8a03
...
...
@@ -32,7 +32,7 @@ q-page.admin-flags
)
q-separator(inset)
.q-pa-md.q-gutter-md
q-card
.shadow-1
q-card
q-list(separator)
template(v-for='editor of editors', :key='editor.id')
q-item(v-if='flagsStore.experimental || !editor.isDisabled')
...
...
ux/src/pages/AdminExtensions.vue
View file @
960a8a03
...
...
@@ -25,7 +25,7 @@ q-page.admin-extensions
q-separator(inset)
.row.q-pa-md.q-col-gutter-md
.col-12
q-card
.shadow-1
q-card
q-list(separator)
q-item(
v-for='(ext, idx) of state.extensions'
...
...
ux/src/pages/AdminFlags.vue
View file @
960a8a03
...
...
@@ -33,7 +33,7 @@ q-page.admin-flags
q-separator(inset)
.row.q-pa-md.q-col-gutter-md
.col-12.col-lg-7
q-card.
shadow-1.
q-py-sm
q-card.q-py-sm
q-item
q-item-section
q-card.bg-negative.text-white.rounded-borders(flat)
...
...
@@ -84,7 +84,7 @@ q-page.admin-flags
unchecked-icon='las la-times'
: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
blueprint-icon(icon='administrative-tools')
q-item-section
...
...
ux/src/pages/AdminGeneral.vue
View file @
960a8a03
...
...
@@ -36,7 +36,7 @@ q-page.admin-general
//- -----------------------
//- Site Info
//- -----------------------
q-card.
shadow-1.
q-pb-sm
q-card.q-pb-sm
q-card-section
.text-subtitle1
{{
t
(
'admin.general.siteInfo'
)
}}
q-item
...
...
@@ -85,7 +85,7 @@ q-page.admin-general
//- -----------------------
//- Footer / Copyright
//- -----------------------
q-card.
shadow-1.
q-pb-sm.q-mt-md
q-card.q-pb-sm.q-mt-md
q-card-section
.text-subtitle1
{{
t
(
'admin.general.footerCopyright'
)
}}
q-item
...
...
@@ -135,7 +135,7 @@ q-page.admin-general
//- -----------------------
//- FEATURES
//- -----------------------
q-card.
shadow-1.
q-pb-sm.q-mt-md
q-card.q-pb-sm.q-mt-md
q-card-section
.text-subtitle1
{{
t
(
'admin.general.features'
)
}}
q-item(tag='label')
...
...
@@ -227,7 +227,7 @@ q-page.admin-general
//- -----------------------
//- URL Handling
//- -----------------------
q-card.
shadow-1.
q-pb-sm.q-mt-md
q-card.q-pb-sm.q-mt-md
q-card-section
.text-subtitle1
{{
t
(
'admin.general.urlHandling'
)
}}
q-item
...
...
@@ -247,7 +247,7 @@ q-page.admin-general
//- -----------------------
//- Logo
//- -----------------------
q-card.
shadow-1.
q-pb-sm
q-card.q-pb-sm
q-card-section
.text-subtitle1
{{
t
(
'admin.general.logo'
)
}}
q-item
...
...
@@ -333,7 +333,7 @@ q-page.admin-general
//- -----------------------
//- 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
.text-subtitle1
{{
t
(
'admin.general.defaults'
)
}}
q-item
...
...
@@ -409,7 +409,7 @@ q-page.admin-general
//- -----------------------
//- 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
.text-subtitle1
{{
t
(
'admin.general.uploads'
)
}}
q-item
...
...
@@ -449,7 +449,7 @@ q-page.admin-general
//- -----------------------
//- 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
.text-subtitle1 SEO
q-item(tag='label')
...
...
ux/src/pages/AdminGroups.vue
View file @
960a8a03
...
...
@@ -40,7 +40,7 @@ q-page.admin-groups
q-separator(inset)
.row.q-pa-md.q-col-gutter-md
.col-12
q-card
.shadow-1
q-card
q-table(
:rows='state.groups'
:columns='headers'
...
...
ux/src/pages/AdminIcons.vue
View file @
960a8a03
...
...
@@ -33,7 +33,7 @@ q-page.admin-icons
q-separator(inset)
.row.q-pa-md.q-col-gutter-md
.col-12
q-card
.shadow-1
q-card
q-card-section
q-card.bg-negative.text-white.rounded-borders(flat)
q-card-section.items-center(horizontal)
...
...
ux/src/pages/AdminInstances.vue
View file @
960a8a03
...
...
@@ -24,7 +24,7 @@ q-page.admin-terminal
)
q-separator(inset)
.q-pa-md.q-gutter-md
q-card
.shadow-1
q-card
q-table(
:rows='state.instances'
:columns='instancesHeaders'
...
...
ux/src/pages/AdminLocale.vue
View file @
960a8a03
...
...
@@ -45,7 +45,7 @@ q-page.admin-locale
//- -----------------------
//- Locale Options
//- -----------------------
q-card.
shadow-1.
q-pb-sm
q-card.q-pb-sm
q-card-section
.text-subtitle1
{{
t
(
'admin.locale.settings'
)
}}
q-item
...
...
@@ -93,7 +93,7 @@ q-page.admin-locale
//- -----------------------
//- Namespacing
//- -----------------------
q
-
card
.
shadow
-
1
.
q
-
pb
-
sm
(
v
-
if
=
'state.namespacing'
)
q
-
card
.
q
-
pb
-
sm
(
v
-
if
=
'state.namespacing'
)
q
-
card
-
section
.
text
-
subtitle1
{{
t
(
'admin.locale.activeNamespaces'
)
}}
...
...
ux/src/pages/AdminLogin.vue
View file @
960a8a03
...
...
@@ -36,7 +36,7 @@ q-page.admin-login
//- -----------------------
//- Experience
//- -----------------------
q-card.
shadow-1.
q-pb-sm
q-card.q-pb-sm
q-card-section
.text-subtitle1
{{
t
(
'admin.login.experience'
)
}}
q-item
...
...
@@ -137,7 +137,7 @@ q-page.admin-login
//- -----------------------
//- Providers
//- -----------------------
q-card.
shadow-1.
q-pb-sm
q-card.q-pb-sm
q-card-section
.text-subtitle1
{{
t
(
'admin.login.providers'
)
}}
q-card-section.admin-login-providers.q-pt-none
...
...
ux/src/pages/AdminMail.vue
View file @
960a8a03
...
...
@@ -36,7 +36,7 @@ q-page.admin-mail
//- -----------------------
//- Configuration
//- -----------------------
q-card.
shadow-1.
q-pb-sm
q-card.q-pb-sm
q-card-section
.text-subtitle1
{{
t
(
'admin.mail.configuration'
)
}}
q-item
...
...
@@ -68,7 +68,7 @@ q-page.admin-mail
//- -----------------------
//- SMTP
//- -----------------------
q-card.
shadow-1.
q-pb-sm.q-mt-md
q-card.q-pb-sm.q-mt-md
q-card-section
.text-subtitle1
{{
t
(
'admin.mail.smtp'
)
}}
q-item
...
...
@@ -168,7 +168,7 @@ q-page.admin-mail
//- -----------------------
//- DKIM
//- -----------------------
q-card.
shadow-1.
q-pb-sm.q-mt-md
q-card.q-pb-sm.q-mt-md
q-card-section
.text-subtitle1
{{
t
(
'admin.mail.dkim'
)
}}
q-item.q-pt-none
...
...
@@ -237,7 +237,7 @@ q-page.admin-mail
//- -----------------------
//- MAIL TEMPLATES
//- -----------------------
q-card.
shadow-1.
q-pb-sm
q-card.q-pb-sm
q-card-section
.text-subtitle1
{{
t
(
'admin.mail.templates'
)
}}
q-list
...
...
@@ -271,7 +271,7 @@ q-page.admin-mail
//- -----------------------
//- SMTP TEST
//- -----------------------
q-card.
shadow-1.
q-pb-sm.q-mt-md
q-card.q-pb-sm.q-mt-md
q-card-section
.text-subtitle1
{{
t
(
'admin.mail.test'
)
}}
q-item
...
...
ux/src/pages/AdminScheduler.vue
View file @
960a8a03
...
...
@@ -52,7 +52,7 @@ q-page.admin-terminal
q-card-section.col-auto.q-pr-none
q-icon(name='las la-info-circle', size='sm')
q-card-section.text-caption
{{
t
(
'admin.scheduler.scheduledNone'
)
}}
q-card
.shadow-1
(v-else)
q-card(v-else)
q-table(
:rows='state.scheduledJobs'
:columns='scheduledJobsHeaders'
...
...
@@ -109,7 +109,7 @@ q-page.admin-terminal
q-card-section.col-auto.q-pr-none
q-icon(name='las la-info-circle', size='sm')
q-card-section.text-caption
{{
t
(
'admin.scheduler.upcomingNone'
)
}}
q-card
.shadow-1
(v-else)
q-card(v-else)
q-table(
:rows='state.upcomingJobs'
:columns='upcomingJobsHeaders'
...
...
@@ -167,7 +167,7 @@ q-page.admin-terminal
q-card-section.col-auto.q-pr-none
q-icon(name='las la-info-circle', size='sm')
q-card-section.text-caption
{{
t
(
'admin.scheduler.'
+
state
.
displayMode
+
'None'
)
}}
q-card
.shadow-1
(v-else)
q-card(v-else)
q-table(
:rows='state.jobs'
:columns='jobsHeaders'
...
...
ux/src/pages/AdminSecurity.vue
View file @
960a8a03
...
...
@@ -36,7 +36,7 @@ q-page.admin-mail
//- -----------------------
//- Security
//- -----------------------
q-card.
shadow-1.
q-pb-sm
q-card.q-pb-sm
q-card-section
.text-subtitle1
{{
t
(
'admin.security.title'
)
}}
q-item.q-pt-none
...
...
@@ -132,7 +132,7 @@ q-page.admin-mail
//- -----------------------
//- HSTS
//- -----------------------
q-card.
shadow-1.
q-pb-sm.q-mt-md
q-card.q-pb-sm.q-mt-md
q-card-section
.text-subtitle1
{{
t
(
'admin.security.hsts'
)
}}
q-item(tag='label', v-ripple)
...
...
@@ -172,7 +172,7 @@ q-page.admin-mail
//- -----------------------
//- Uploads
//- -----------------------
q-card.
shadow-1.
q-pb-sm
q-card.q-pb-sm
q-card-section
.text-subtitle1
{{
t
(
'admin.security.uploads'
)
}}
q-item.q-pt-none
...
...
@@ -226,7 +226,7 @@ q-page.admin-mail
//- -----------------------
//- CORS
//- -----------------------
q-card.
shadow-1.
q-pb-sm.q-mt-md
q-card.q-pb-sm.q-mt-md
q-card-section
.text-subtitle1
{{
t
(
'admin.security.cors'
)
}}
q-item
...
...
@@ -279,7 +279,7 @@ q-page.admin-mail
//- -----------------------
//- JWT
//- -----------------------
q-card.
shadow-1.
q-pb-sm.q-mt-md
q-card.q-pb-sm.q-mt-md
q-card-section
.text-subtitle1
{{
t
(
'admin.security.jwt'
)
}}
q-item
...
...
ux/src/pages/AdminSites.vue
View file @
960a8a03
...
...
@@ -31,7 +31,7 @@ q-page.admin-locale
q-separator(inset)
.row.q-pa-md.q-col-gutter-md
.col-12
q-card
.shadow-1
q-card
q-list(separator)
q-item(
v-for='site of adminStore.sites'
...
...
ux/src/pages/AdminStorage.vue
View file @
960a8a03
...
...
@@ -77,7 +77,7 @@ q-page.admin-storage
//- -----------------------
//- 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
.text-subtitle1
{{
t
(
'admin.storage.setup'
)
}}
.text-body2.text-grey
{{
t
(
'admin.storage.setupHint'
)
}}
...
...
@@ -167,7 +167,7 @@ q-page.admin-storage
@click='setupGitHubStep(`verify`)'
: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
.text-subtitle1
{{
t
(
'admin.storage.setup'
)
}}
.text-body2.text-grey
{{
t
(
'admin.storage.setupConfiguredHint'
)
}}
...
...
@@ -189,7 +189,7 @@ q-page.admin-storage
//- -----------------------
//- Content Types
//- -----------------------
q-card.
shadow-1.
q-pb-sm
q-card.q-pb-sm
q-card-section
.text-subtitle1
{{
t
(
'admin.storage.contentTypes'
)
}}
.text-body2.text-grey
{{
t
(
'admin.storage.contentTypesHint'
)
}}
...
...
@@ -262,7 +262,7 @@ q-page.admin-storage
//- -----------------------
//- Content Delivery
//- -----------------------
q-card.
shadow-1.
q-pb-sm.q-mt-md
q-card.q-pb-sm.q-mt-md
q-card-section
.text-subtitle1
{{
t
(
'admin.storage.assetDelivery'
)
}}
.text-body2.text-grey
{{
t
(
'admin.storage.assetDeliveryHint'
)
}}
...
...
@@ -294,7 +294,7 @@ q-page.admin-storage
//- -----------------------
//- Configuration
//- -----------------------
q-card.
shadow-1.
q-pb-sm.q-mt-md
q-card.q-pb-sm.q-mt-md
q-card-section
.text-subtitle1
{{
t
(
'admin.storage.config'
)
}}
q-banner.q-mt-md(
...
...
@@ -367,7 +367,7 @@ q-page.admin-storage
//- -----------------------
//- 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
.text-subtitle1
{{
t
(
'admin.storage.sync'
)
}}
q-banner.q-mt-md(
...
...
@@ -378,7 +378,7 @@ q-page.admin-storage
//- -----------------------
//- Actions
//- -----------------------
q-card.
shadow-1.
q-pb-sm.q-mt-md
q-card.q-pb-sm.q-mt-md
q-card-section
.text-subtitle1
{{
t
(
'admin.storage.actions'
)
}}
q-banner.q-mt-md(
...
...
ux/src/pages/AdminSystem.vue
View file @
960a8a03
...
...
@@ -37,7 +37,7 @@ q-page.admin-system
//- -----------------------
//- WIKI.JS
//- -----------------------
q-card.q-pb-sm
.shadow-1
q-card.q-pb-sm
q-card-section
.text-subtitle1 Wiki.js
q-item
...
...
@@ -69,7 +69,7 @@ q-page.admin-system
//- CLIENT
//- -----------------------
q-no-ssr
q-card.q-mt-md.q-pb-sm
.shadow-1
q-card.q-mt-md.q-pb-sm
q-card-section
.text-subtitle1
{{
t
(
'admin.system.client'
)
}}
q-item
...
...
@@ -116,7 +116,7 @@ q-page.admin-system
//- -----------------------
//- ENGINES
//- -----------------------
q-card.q-pb-sm
.shadow-1
q-card.q-pb-sm
q-card-section
.text-subtitle1
{{
t
(
'admin.system.engines'
)
}}
q-item
...
...
@@ -146,7 +146,7 @@ q-page.admin-system
//- -----------------------
//- HOST INFORMATION
//- -----------------------
q-card.q-mt-md.q-pb-sm
.shadow-1
q-card.q-mt-md.q-pb-sm
q-card-section
.text-subtitle1
{{
t
(
'admin.system.hostInfo'
)
}}
q-item
...
...
@@ -450,6 +450,11 @@ Total RAM: ${state.info.ramTotal}`
padding
:
8px
12px
;
border-radius
:
4px
;
font-family
:
'Roboto Mono'
,
Consolas
,
"Liberation Mono"
,
Courier
,
monospace
;
@at-root
.body--dark
&
{
background-color
:
$dark-4
;
color
:
#FFF
;
}
}
}
</
style
>
ux/src/pages/AdminTerminal.vue
View file @
960a8a03
...
...
@@ -43,7 +43,7 @@ q-page.admin-terminal
)
q-separator(inset)
.q-pa-md.q-gutter-md
q-card
.shadow-1
q-card
.admin-terminal-term(ref='termDiv')
</
template
>
...
...
ux/src/pages/AdminTheme.vue
View file @
960a8a03
...
...
@@ -36,7 +36,7 @@ q-page.admin-theme
//- -----------------------
//- Theme Options
//- -----------------------
q-card.
shadow-1.
q-pb-sm
q-card.q-pb-sm
q-card-section.flex.items-center
.text-subtitle1
{{
t
(
'admin.theme.appearance'
)
}}
q-space
...
...
@@ -90,7 +90,7 @@ q-page.admin-theme
//- -----------------------
//- Theme Layout
//- -----------------------
q-card.
shadow-1.
q-pb-sm.q-mt-md
q-card.q-pb-sm.q-mt-md
q-card-section
.text-subtitle1
{{
t
(
'admin.theme.layout'
)
}}
q-item
...
...
@@ -170,7 +170,7 @@ q-page.admin-theme
//- -----------------------
//- Fonts
//- -----------------------
q-card.
shadow-1.
q-pb-sm
q-card.q-pb-sm
q-card-section.flex.items-center
.text-subtitle1
{{
t
(
'admin.theme.fonts'
)
}}
q-space
...
...
@@ -216,7 +216,7 @@ q-page.admin-theme
//- -----------------------
//- Code Injection
//- -----------------------
q-card.
shadow-1.
q-pb-sm.q-mt-md
q-card.q-pb-sm.q-mt-md
q-card-section
.text-subtitle1
{{
t
(
'admin.theme.codeInjection'
)
}}
q-item
...
...
@@ -471,12 +471,7 @@ async function save () {
siteStore
.
$patch
({
theme
:
patchTheme
})
$q
.
dark
.
set
(
state
.
config
.
dark
)
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
)
EVENT_BUS
.
emit
(
'applyTheme'
)
}
$q
.
notify
({
type
:
'positive'
,
...
...
ux/src/pages/AdminUsers.vue
View file @
960a8a03
...
...
@@ -41,7 +41,7 @@ q-page.admin-groups
q-separator(inset)
.row.q-pa-md.q-col-gutter-md
.col-12
q-card
.shadow-1
q-card
q-table(
:rows='state.users'
:columns='headers'
...
...
@@ -246,7 +246,7 @@ function createUser () {
$q
.
dialog
({
component
:
UserCreateDialog
}).
onOk
(()
=>
{
this
.
load
()
load
()
})
}
...
...
ux/src/pages/AdminUtilities.vue
View file @
960a8a03
...
...
@@ -17,7 +17,7 @@ q-page.admin-utilities
)
q-separator(inset)
.q-pa-md.q-gutter-md
q-card
.shadow-1
q-card
q-list(separator)
q-item
blueprint-icon(icon='disconnected', :hue-rotate='45')
...
...
ux/src/pages/Index.vue
View file @
960a8a03
...
...
@@ -34,7 +34,7 @@ q-page.column
v-if='editorStore.isActive'
)
component(:is='editorComponents[editorStore.editor]')
q-scroll-area(
q-scroll-area
.page-container-scrl
(
v-else
:thumb-style='thumbStyle'
:bar-style='barStyle'
...
...
@@ -165,6 +165,7 @@ import { useEditorStore } from 'src/stores/editor'
import
{
useFlagsStore
}
from
'src/stores/flags'
import
{
usePageStore
}
from
'src/stores/page'
import
{
useSiteStore
}
from
'src/stores/site'
import
{
useUserStore
}
from
'src/stores/user'
// COMPONENTS
...
...
@@ -195,6 +196,7 @@ const editorStore = useEditorStore()
const
flagsStore
=
useFlagsStore
()
const
pageStore
=
usePageStore
()
const
siteStore
=
useSiteStore
()
const
userStore
=
useUserStore
()
// ROUTER
...
...
@@ -269,7 +271,13 @@ watch(() => route.path, async (newValue) => {
}
catch
(
err
)
{
if
(
err
.
message
===
'ERR_PAGE_NOT_FOUND'
)
{
if
(
newValue
===
'/'
)
{
if
(
!
userStore
.
authenticated
)
{
router
.
push
(
'/login'
)
}
else
if
(
!
userStore
.
can
(
'write:pages'
))
{
router
.
replace
(
'/_error/unauthorized'
)
}
else
{
siteStore
.
overlay
=
'Welcome'
}
}
else
{
$q
.
notify
({
type
:
'negative'
,
...
...
@@ -369,6 +377,10 @@ function refreshTocExpanded (baseToc, lvl) {
// @at-root .body--dark & {
// border-top: 1px solid $dark-6;
// }
.page-container-scrl
>
.q-scrollarea__container
>
.q-scrollarea__content
{
width
:
100%
;
}
}
.page-sidebar
{
flex
:
0
0
300px
;
...
...
ux/src/stores/page.js
View file @
960a8a03
...
...
@@ -369,6 +369,10 @@ export const usePageStore = defineStore('page', {
tocDepth
:
pick
(
pageData
.
tocDepth
,
[
'min'
,
'max'
])
})
editorStore
.
$patch
({
mode
:
'edit'
})
this
.
router
.
replace
(
`/
${
this
.
path
}
`
)
}
else
{
const
resp
=
await
APOLLO_CLIENT
.
mutate
({
...
...
@@ -390,7 +394,8 @@ export const usePageStore = defineStore('page', {
`
,
variables
:
{
id
:
this
.
id
,
patch
:
pick
(
this
,
[
patch
:
{
...
pick
(
this
,
[
'allowComments'
,
'allowContributions'
,
'allowRatings'
,
...
...
@@ -415,7 +420,9 @@ export const usePageStore = defineStore('page', {
'tags'
,
'title'
,
'tocDepth'
])
]),
reasonForChange
:
editorStore
.
reasonForChange
}
}
})
const
result
=
resp
?.
data
?.
updatePage
?.
operation
??
{}
...
...
@@ -427,7 +434,8 @@ export const usePageStore = defineStore('page', {
const
curDate
=
DateTime
.
utc
()
editorStore
.
$patch
({
lastChangeTimestamp
:
curDate
,
lastSaveTimestamp
:
curDate
lastSaveTimestamp
:
curDate
,
reasonForChange
:
''
})
}
catch
(
err
)
{
console
.
warn
(
err
)
...
...
ux/src/stores/user.js
View file @
960a8a03
...
...
@@ -5,6 +5,8 @@ import gql from 'graphql-tag'
import
{
DateTime
}
from
'luxon'
import
{
getAccessibleColor
}
from
'src/helpers/accessibility'
import
{
useSiteStore
}
from
'./site'
export
const
useUserStore
=
defineStore
(
'user'
,
{
state
:
()
=>
({
id
:
'10000000-0000-4000-8000-000000000001'
,
...
...
@@ -18,6 +20,7 @@ export const useUserStore = defineStore('user', {
appearance
:
'site'
,
cvd
:
'none'
,
permissions
:
[],
pagePermissions
:
[],
iat
:
0
,
exp
:
null
,
authenticated
:
false
,
...
...
@@ -27,6 +30,9 @@ export const useUserStore = defineStore('user', {
getters
:
{},
actions
:
{
async
refreshAuth
()
{
if
(
this
.
exp
&&
this
.
exp
<
DateTime
.
now
())
{
return
}
const
jwtCookie
=
Cookies
.
get
(
'jwt'
)
if
(
jwtCookie
)
{
try
{
...
...
@@ -38,6 +44,7 @@ export const useUserStore = defineStore('user', {
this
.
token
=
jwtCookie
if
(
this
.
exp
<=
DateTime
.
utc
())
{
console
.
info
(
'Token has expired. Attempting renew...'
)
// TODO: Renew token
}
else
{
this
.
authenticated
=
true
}
...
...
@@ -69,6 +76,7 @@ export const useUserStore = defineStore('user', {
name
}
}
userPermissions
}
`
,
variables
:
{
...
...
@@ -90,13 +98,71 @@ export const useUserStore = defineStore('user', {
this
.
timeFormat
=
resp
.
prefs
.
timeFormat
||
'12h'
this
.
appearance
=
resp
.
prefs
.
appearance
||
'site'
this
.
cvd
=
resp
.
prefs
.
cvd
||
'none'
this
.
permissions
=
respRaw
.
data
.
userPermissions
||
[]
this
.
profileLoaded
=
true
}
catch
(
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
)
{
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
}
!`
)
}
}
}
})
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment