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
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