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
291fe262
Unverified
Commit
291fe262
authored
Jan 21, 2024
by
NGPixel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: asset rename + asset delete dialogs + linting fixes
parent
f8bc9e8c
Show whitespace changes
Inline
Side-by-side
Showing
42 changed files
with
597 additions
and
151 deletions
+597
-151
settings.json
.vscode/settings.json
+1
-1
asset.mjs
server/graph/resolvers/asset.mjs
+73
-64
asset.graphql
server/graph/schemas/asset.graphql
+3
-3
en.json
server/locales/en.json
+9
-0
assets.mjs
server/models/assets.mjs
+3
-3
pages.mjs
server/models/pages.mjs
+11
-23
package.json
server/package.json
+1
-0
pnpm-lock.yaml
server/pnpm-lock.yaml
+61
-0
home.md
server/templates/demo/home.md
+31
-0
.eslintrc.js
ux/.eslintrc.js
+11
-1
jsconfig.json
ux/jsconfig.json
+1
-1
package.json
ux/package.json
+2
-1
pnpm-lock.yaml
ux/pnpm-lock.yaml
+23
-0
ultraviolet-image.svg
ux/public/_assets/icons/ultraviolet-image.svg
+2
-0
ApiKeyCopyDialog.vue
ux/src/components/ApiKeyCopyDialog.vue
+2
-2
ApiKeyCreateDialog.vue
ux/src/components/ApiKeyCreateDialog.vue
+2
-2
AssetDeleteDialog.vue
ux/src/components/AssetDeleteDialog.vue
+109
-0
AssetRenameDialog.vue
ux/src/components/AssetRenameDialog.vue
+165
-0
FileManager.vue
ux/src/components/FileManager.vue
+51
-18
FooterNav.vue
ux/src/components/FooterNav.vue
+2
-0
HeaderSearch.vue
ux/src/components/HeaderSearch.vue
+1
-0
IconPickerDialog.vue
ux/src/components/IconPickerDialog.vue
+4
-2
LocaleSelectorMenu.vue
ux/src/components/LocaleSelectorMenu.vue
+1
-0
NavSidebar.vue
ux/src/components/NavSidebar.vue
+1
-0
PageActionsCol.vue
ux/src/components/PageActionsCol.vue
+4
-1
PageTags.vue
ux/src/components/PageTags.vue
+2
-1
SocialSharingMenu.vue
ux/src/components/SocialSharingMenu.vue
+1
-1
TreeBrowserDialog.vue
ux/src/components/TreeBrowserDialog.vue
+6
-6
AdminLayout.vue
ux/src/layouts/AdminLayout.vue
+1
-0
MainLayout.vue
ux/src/layouts/MainLayout.vue
+0
-1
AdminExtensions.vue
ux/src/pages/AdminExtensions.vue
+1
-1
AdminFlags.vue
ux/src/pages/AdminFlags.vue
+0
-1
AdminLocale.vue
ux/src/pages/AdminLocale.vue
+1
-1
AdminLogin.vue
ux/src/pages/AdminLogin.vue
+0
-1
AdminMetrics.vue
ux/src/pages/AdminMetrics.vue
+2
-2
AdminSystem.vue
ux/src/pages/AdminSystem.vue
+0
-1
AdminTheme.vue
ux/src/pages/AdminTheme.vue
+1
-1
AdminUsers.vue
ux/src/pages/AdminUsers.vue
+0
-1
AdminUtilities.vue
ux/src/pages/AdminUtilities.vue
+0
-6
Index.vue
ux/src/pages/Index.vue
+1
-1
Search.vue
ux/src/pages/Search.vue
+6
-3
site.js
ux/src/stores/site.js
+1
-1
No files found.
.vscode/settings.json
View file @
291fe262
...
@@ -8,7 +8,7 @@
...
@@ -8,7 +8,7 @@
"vue"
"vue"
],
],
"editor.codeActionsOnSave"
:
{
"editor.codeActionsOnSave"
:
{
"source.fixAll.eslint"
:
true
"source.fixAll.eslint"
:
"explicit"
},
},
"i18n-ally.localesPaths"
:
[
"i18n-ally.localesPaths"
:
[
"server/locales"
,
"server/locales"
,
...
...
server/graph/resolvers/asset.mjs
View file @
291fe262
import
_
from
'lodash-es'
import
_
from
'lodash-es'
import
sanitize
from
'sanitize-filename'
import
sanitize
from
'sanitize-filename'
import
{
generateError
,
generateSuccess
}
from
'../../helpers/graph.mjs'
import
{
generateError
,
generateSuccess
}
from
'../../helpers/graph.mjs'
import
{
decodeFolderPath
,
decodeTreePath
,
generateHash
}
from
'../../helpers/common.mjs'
import
path
from
'node:path'
import
path
from
'node:path'
import
fs
from
'fs-extra'
import
fs
from
'fs-extra'
import
{
v4
as
uuid
}
from
'uuid'
import
{
v4
as
uuid
}
from
'uuid'
...
@@ -9,7 +10,12 @@ import { pipeline } from 'node:stream/promises'
...
@@ -9,7 +10,12 @@ import { pipeline } from 'node:stream/promises'
export
default
{
export
default
{
Query
:
{
Query
:
{
async
assetById
(
obj
,
args
,
context
)
{
async
assetById
(
obj
,
args
,
context
)
{
return
null
const
asset
=
await
WIKI
.
db
.
assets
.
query
().
findById
(
args
.
id
)
if
(
asset
)
{
return
asset
}
else
{
throw
new
Error
(
'ERR_ASSET_NOT_FOUND'
)
}
}
}
},
},
Mutation
:
{
Mutation
:
{
...
@@ -18,75 +24,75 @@ export default {
...
@@ -18,75 +24,75 @@ export default {
*/
*/
async
renameAsset
(
obj
,
args
,
context
)
{
async
renameAsset
(
obj
,
args
,
context
)
{
try
{
try
{
const
filename
=
sanitize
(
args
.
file
n
ame
).
toLowerCase
()
const
filename
=
sanitize
(
args
.
file
N
ame
).
toLowerCase
()
const
asset
=
await
WIKI
.
db
.
assets
.
query
().
findById
(
args
.
id
)
const
asset
=
await
WIKI
.
db
.
assets
.
query
().
findById
(
args
.
id
)
if
(
asset
)
{
const
treeItem
=
await
WIKI
.
db
.
tree
.
query
().
findById
(
args
.
id
)
if
(
asset
&&
treeItem
)
{
// Check for extension mismatch
// Check for extension mismatch
if
(
!
_
.
endsWith
(
filename
,
asset
.
e
xt
))
{
if
(
!
_
.
endsWith
(
filename
,
asset
.
fileE
xt
))
{
throw
new
WIKI
.
Error
.
AssetRenameInvalidExt
(
)
throw
new
Error
(
'ERR_ASSET_EXT_MISMATCH'
)
}
}
// Check for non-dot files changing to dotfile
// Check for non-dot files changing to dotfile
if
(
asset
.
ext
.
length
>
0
&&
filename
.
length
-
asset
.
e
xt
.
length
<
1
)
{
if
(
asset
.
fileExt
.
length
>
0
&&
filename
.
length
-
asset
.
fileE
xt
.
length
<
1
)
{
throw
new
WIKI
.
Error
.
AssetRenameInvalid
(
)
throw
new
Error
(
'ERR_ASSET_INVALID_DOTFILE'
)
}
}
// Check for collision
// Check for collision
const
assetCollision
=
await
WIKI
.
db
.
assets
.
query
().
where
({
const
assetCollision
=
await
WIKI
.
db
.
tree
.
query
().
where
({
f
ilename
,
f
olderPath
:
treeItem
.
folderPath
,
f
olderId
:
asset
.
folderId
f
ileName
:
filename
}).
first
()
}).
first
()
if
(
assetCollision
)
{
if
(
assetCollision
)
{
throw
new
WIKI
.
Error
.
AssetRenameCollision
()
throw
new
Error
(
'ERR_ASSET_ALREADY_EXISTS'
)
}
// Get asset folder path
let
hierarchy
=
[]
if
(
asset
.
folderId
)
{
hierarchy
=
await
WIKI
.
db
.
assetFolders
.
getHierarchy
(
asset
.
folderId
)
}
}
// Check source asset permissions
// Check source asset permissions
const
assetSourcePath
=
(
asset
.
folderId
)
?
hierarchy
.
map
(
h
=>
h
.
slug
).
join
(
'/'
)
+
`/
${
asset
.
filename
}
`
:
asset
.
filen
ame
const
assetSourcePath
=
(
treeItem
.
folderPath
)
?
decodeTreePath
(
decodeFolderPath
(
treeItem
.
folderPath
))
+
`/
${
treeItem
.
fileName
}
`
:
treeItem
.
fileN
ame
if
(
!
WIKI
.
auth
.
checkAccess
(
context
.
req
.
user
,
[
'manage:assets'
],
{
path
:
assetSourcePath
}))
{
if
(
!
WIKI
.
auth
.
checkAccess
(
context
.
req
.
user
,
[
'manage:assets'
],
{
path
:
assetSourcePath
}))
{
throw
new
WIKI
.
Error
.
AssetRenameForbidden
(
)
throw
new
Error
(
'ERR_FORBIDDEN'
)
}
}
// Check target asset permissions
// Check target asset permissions
const
assetTargetPath
=
(
asset
.
folderId
)
?
hierarchy
.
map
(
h
=>
h
.
slug
).
join
(
'/'
)
+
`/
${
filename
}
`
:
filename
const
assetTargetPath
=
(
treeItem
.
folderPath
)
?
decodeTreePath
(
decodeFolderPath
(
treeItem
.
folderPath
)
)
+
`/
${
filename
}
`
:
filename
if
(
!
WIKI
.
auth
.
checkAccess
(
context
.
req
.
user
,
[
'write:assets'
],
{
path
:
assetTargetPath
}))
{
if
(
!
WIKI
.
auth
.
checkAccess
(
context
.
req
.
user
,
[
'write:assets'
],
{
path
:
assetTargetPath
}))
{
throw
new
WIKI
.
Error
.
AssetRenameTargetForbidden
(
)
throw
new
Error
(
'ERR_TARGET_FORBIDDEN'
)
}
}
// Update filename + hash
// Update filename + hash
const
fileHash
=
''
// assetHelper.
generateHash(assetTargetPath)
const
itemHash
=
generateHash
(
assetTargetPath
)
await
WIKI
.
db
.
assets
.
query
().
patch
({
await
WIKI
.
db
.
assets
.
query
().
patch
({
filename
:
filename
,
fileName
:
filename
hash
:
fileHash
}).
findById
(
asset
.
id
)
}).
findById
(
args
.
id
)
await
WIKI
.
db
.
tree
.
query
().
patch
({
// Delete old asset cache
fileName
:
filename
,
await
asset
.
deleteAssetCache
()
title
:
filename
,
hash
:
itemHash
// Rename in Storage
}).
findById
(
treeItem
.
id
)
await
WIKI
.
db
.
storage
.
assetEvent
({
event
:
'renamed'
,
// TODO: Delete old asset cache
asset
:
{
WIKI
.
events
.
outbound
.
emit
(
'purgeItemCache'
,
itemHash
)
...
asset
,
path
:
assetSourcePath
,
// TODO: Rename in Storage
destinationPath
:
assetTargetPath
,
// await WIKI.db.storage.assetEvent({
moveAuthorId
:
context
.
req
.
user
.
id
,
// event: 'renamed',
moveAuthorName
:
context
.
req
.
user
.
name
,
// asset: {
moveAuthorEmail
:
context
.
req
.
user
.
email
// ...asset,
}
// path: assetSourcePath,
})
// destinationPath: assetTargetPath,
// moveAuthorId: context.req.user.id,
// moveAuthorName: context.req.user.name,
// moveAuthorEmail: context.req.user.email
// }
// })
return
{
return
{
responseResult
:
generateSuccess
(
'Asset has been renamed successfully.'
)
operation
:
generateSuccess
(
'Asset has been renamed successfully.'
)
}
}
}
else
{
}
else
{
throw
new
WIKI
.
Error
.
AssetInvalid
(
)
throw
new
Error
(
'ERR_INVALID_ASSET'
)
}
}
}
catch
(
err
)
{
}
catch
(
err
)
{
return
generateError
(
err
)
return
generateError
(
err
)
...
@@ -97,35 +103,38 @@ export default {
...
@@ -97,35 +103,38 @@ export default {
*/
*/
async
deleteAsset
(
obj
,
args
,
context
)
{
async
deleteAsset
(
obj
,
args
,
context
)
{
try
{
try
{
const
asset
=
await
WIKI
.
db
.
assets
.
query
().
findById
(
args
.
id
)
const
treeItem
=
await
WIKI
.
db
.
tree
.
query
().
findById
(
args
.
id
)
if
(
asset
)
{
if
(
treeItem
)
{
// Check permissions
// Check permissions
const
assetPath
=
await
asset
.
getAssetPath
()
const
assetPath
=
(
treeItem
.
folderPath
)
?
decodeTreePath
(
decodeFolderPath
(
treeItem
.
folderPath
))
+
`/
${
treeItem
.
fileName
}
`
:
treeItem
.
fileName
if
(
!
WIKI
.
auth
.
checkAccess
(
context
.
req
.
user
,
[
'manage:assets'
],
{
path
:
assetPath
}))
{
if
(
!
WIKI
.
auth
.
checkAccess
(
context
.
req
.
user
,
[
'manage:assets'
],
{
path
:
assetPath
}))
{
throw
new
WIKI
.
Error
.
AssetDeleteForbidden
(
)
throw
new
Error
(
'ERR_FORBIDDEN'
)
}
}
await
WIKI
.
db
.
knex
(
'assetData'
).
where
(
'id'
,
args
.
id
).
del
()
// Delete from DB
await
WIKI
.
db
.
assets
.
query
().
deleteById
(
args
.
id
)
await
WIKI
.
db
.
assets
.
query
().
deleteById
(
treeItem
.
id
)
await
asset
.
deleteAssetCache
(
)
await
WIKI
.
db
.
tree
.
query
().
deleteById
(
treeItem
.
id
)
// Delete from Storage
// TODO: Delete asset cache
await
WIKI
.
db
.
storage
.
assetEvent
({
WIKI
.
events
.
outbound
.
emit
(
'purgeItemCache'
,
treeItem
.
hash
)
event
:
'deleted'
,
asset
:
{
// TODO: Delete from Storage
...
asset
,
// await WIKI.db.storage.assetEvent({
path
:
assetPath
,
// event: 'deleted',
authorId
:
context
.
req
.
user
.
id
,
// asset: {
authorName
:
context
.
req
.
user
.
name
,
// ...asset,
authorEmail
:
context
.
req
.
user
.
email
// path: assetPath,
}
// authorId: context.req.user.id,
})
// authorName: context.req.user.name,
// authorEmail: context.req.user.email
// }
// })
return
{
return
{
responseResult
:
generateSuccess
(
'Asset has been deleted successfully.'
)
operation
:
generateSuccess
(
'Asset has been deleted successfully.'
)
}
}
}
else
{
}
else
{
throw
new
WIKI
.
Error
.
AssetInvalid
(
)
throw
new
Error
(
'ERR_INVALID_ASSET'
)
}
}
}
catch
(
err
)
{
}
catch
(
err
)
{
return
generateError
(
err
)
return
generateError
(
err
)
...
@@ -373,7 +382,7 @@ export default {
...
@@ -373,7 +382,7 @@ export default {
try
{
try
{
await
WIKI
.
db
.
assets
.
flushTempUploads
()
await
WIKI
.
db
.
assets
.
flushTempUploads
()
return
{
return
{
responseResult
:
generateSuccess
(
'Temporary Uploads have been flushed successfully.'
)
operation
:
generateSuccess
(
'Temporary Uploads have been flushed successfully.'
)
}
}
}
catch
(
err
)
{
}
catch
(
err
)
{
return
generateError
(
err
)
return
generateError
(
err
)
...
...
server/graph/schemas/asset.graphql
View file @
291fe262
...
@@ -5,13 +5,13 @@
...
@@ -5,13 +5,13 @@
extend
type
Query
{
extend
type
Query
{
assetById
(
assetById
(
id
:
UUID
!
id
:
UUID
!
):
[
AssetItem
]
):
AssetItem
}
}
extend
type
Mutation
{
extend
type
Mutation
{
renameAsset
(
renameAsset
(
id
:
UUID
!
id
:
UUID
!
file
n
ame
:
String
!
file
N
ame
:
String
!
):
DefaultResponse
):
DefaultResponse
deleteAsset
(
deleteAsset
(
...
@@ -39,7 +39,7 @@ extend type Mutation {
...
@@ -39,7 +39,7 @@ extend type Mutation {
type
AssetItem
{
type
AssetItem
{
id
:
UUID
id
:
UUID
file
n
ame
:
String
file
N
ame
:
String
ext
:
String
ext
:
String
kind
:
AssetKind
kind
:
AssetKind
mime
:
String
mime
:
String
...
...
server/locales/en.json
View file @
291fe262
...
@@ -1643,6 +1643,13 @@
...
@@ -1643,6 +1643,13 @@
"fileman.aiFileType"
:
"Adobe Illustrator Document"
,
"fileman.aiFileType"
:
"Adobe Illustrator Document"
,
"fileman.aifFileType"
:
"AIF Audio File"
,
"fileman.aifFileType"
:
"AIF Audio File"
,
"fileman.apkFileType"
:
"Android Package"
,
"fileman.apkFileType"
:
"Android Package"
,
"fileman.assetDelete"
:
"Delete Asset"
,
"fileman.assetDeleteConfirm"
:
"Are you sure you want to delete {name}?"
,
"fileman.assetDeleteId"
:
"Asset ID {id}"
,
"fileman.assetDeleteSuccess"
:
"Asset deleted successfully."
,
"fileman.assetFileName"
:
"Asset Name"
,
"fileman.assetFileNameHint"
:
"Filename of the asset, including the file extension."
,
"fileman.assetRename"
:
"Rename Asset"
,
"fileman.aviFileType"
:
"AVI Video File"
,
"fileman.aviFileType"
:
"AVI Video File"
,
"fileman.binFileType"
:
"Binary File"
,
"fileman.binFileType"
:
"Binary File"
,
"fileman.bz2FileType"
:
"BZIP2 Archive"
,
"fileman.bz2FileType"
:
"BZIP2 Archive"
,
...
@@ -1698,6 +1705,8 @@
...
@@ -1698,6 +1705,8 @@
"fileman.pptxFileType"
:
"Microsoft Powerpoint Presentation"
,
"fileman.pptxFileType"
:
"Microsoft Powerpoint Presentation"
,
"fileman.psdFileType"
:
"Adobe Photoshop Document"
,
"fileman.psdFileType"
:
"Adobe Photoshop Document"
,
"fileman.rarFileType"
:
"RAR Archive"
,
"fileman.rarFileType"
:
"RAR Archive"
,
"fileman.renameAssetInvalid"
:
"Asset name is invalid."
,
"fileman.renameAssetSuccess"
:
"Asset renamed successfully"
,
"fileman.renameFolderInvalidData"
:
"One or more fields are invalid."
,
"fileman.renameFolderInvalidData"
:
"One or more fields are invalid."
,
"fileman.renameFolderSuccess"
:
"Folder renamed successfully."
,
"fileman.renameFolderSuccess"
:
"Folder renamed successfully."
,
"fileman.searchFolder"
:
"Search folder..."
,
"fileman.searchFolder"
:
"Search folder..."
,
...
...
server/models/assets.mjs
View file @
291fe262
...
@@ -47,13 +47,13 @@ export class Asset extends Model {
...
@@ -47,13 +47,13 @@ export class Asset extends Model {
async
$beforeUpdate
(
opt
,
context
)
{
async
$beforeUpdate
(
opt
,
context
)
{
await
super
.
$beforeUpdate
(
opt
,
context
)
await
super
.
$beforeUpdate
(
opt
,
context
)
this
.
updatedAt
=
moment
.
utc
().
toISOString
()
this
.
updatedAt
=
new
Date
().
toISOString
()
}
}
async
$beforeInsert
(
context
)
{
async
$beforeInsert
(
context
)
{
await
super
.
$beforeInsert
(
context
)
await
super
.
$beforeInsert
(
context
)
this
.
createdAt
=
moment
.
utc
().
toISOString
()
this
.
createdAt
=
new
Date
().
toISOString
()
this
.
updatedAt
=
moment
.
utc
().
toISOString
()
this
.
updatedAt
=
new
Date
().
toISOString
()
}
}
async
getAssetPath
()
{
async
getAssetPath
()
{
...
...
server/models/pages.mjs
View file @
291fe262
...
@@ -13,19 +13,16 @@ import CleanCSS from 'clean-css'
...
@@ -13,19 +13,16 @@ import CleanCSS from 'clean-css'
import
TurndownService
from
'turndown'
import
TurndownService
from
'turndown'
import
{
gfm
as
turndownPluginGfm
}
from
'@joplin/turndown-plugin-gfm'
import
{
gfm
as
turndownPluginGfm
}
from
'@joplin/turndown-plugin-gfm'
import
cheerio
from
'cheerio'
import
cheerio
from
'cheerio'
import
matter
from
'gray-matter'
import
{
Locale
}
from
'./locales.mjs'
import
{
PageLink
}
from
'./pageLinks.mjs'
import
{
PageLink
}
from
'./pageLinks.mjs'
import
{
Tag
}
from
'./tags.mjs'
import
{
User
}
from
'./users.mjs'
import
{
User
}
from
'./users.mjs'
const
pageRegex
=
/^
[
a-zA-Z0-9-_
/]
*$/
const
pageRegex
=
/^
[
a-zA-Z0-9-_
/]
*$/
const
aliasRegex
=
/^
[
a-zA-Z0-9-_
]
*$/
const
aliasRegex
=
/^
[
a-zA-Z0-9-_
]
*$/
const
frontmatterRegex
=
{
const
frontmatterRegex
=
{
html
:
/^
(
<!-
{2}(?:\n
|
\r)([\w\W]
+
?)(?:\n
|
\r)
-
{2}
>
)?(?:\n
|
\r)
*
([\w\W]
*
)
*/
,
html
:
/^
(
<!-
{2}(?:\n
|
\r)([\w\W]
+
?)(?:\n
|
\r)
-
{2}
>
)?(?:\n
|
\r)
*
([\w\W]
*
)
*/
legacy
:
/^
(
<!-- TITLE:
?([\w\W]
+
?)
?
-
{2}
>
)?(?:\n
|
\r)?(
<!-- SUBTITLE:
?([\w\W]
+
?)
?
-
{2}
>
)?(?:\n
|
\r)
*
([\w\W]
*
)
*/i
,
markdown
:
/^
(
-
{3}(?:\n
|
\r)([\w\W]
+
?)(?:\n
|
\r)
-
{3})?(?:\n
|
\r)
*
([\w\W]
*
)
*/
}
}
/**
/**
...
@@ -178,30 +175,20 @@ export class Page extends Model {
...
@@ -178,30 +175,20 @@ export class Page extends Model {
* @returns {Object} Parsed Page Metadata with Raw Content
* @returns {Object} Parsed Page Metadata with Raw Content
*/
*/
static
parseMetadata
(
raw
,
contentType
)
{
static
parseMetadata
(
raw
,
contentType
)
{
let
result
try
{
try
{
switch
(
contentType
)
{
switch
(
contentType
)
{
case
'markdown'
:
case
'markdown'
:
{
result
=
frontmatterRegex
.
markdown
.
exec
(
raw
)
const
result
=
matter
(
raw
)
if
(
result
[
2
])
{
if
(
!
result
?.
isEmpty
)
{
return
{
...
yaml
.
safeLoad
(
result
[
2
]),
content
:
result
[
3
]
}
}
else
{
// Attempt legacy v1 format
result
=
frontmatterRegex
.
legacy
.
exec
(
raw
)
if
(
result
[
2
])
{
return
{
return
{
title
:
result
[
2
],
content
:
result
.
content
,
description
:
result
[
4
],
...
result
.
data
content
:
result
[
5
]
}
}
}
}
}
break
break
case
'html'
:
}
result
=
frontmatterRegex
.
html
.
exec
(
raw
)
case
'html'
:
{
const
result
=
frontmatterRegex
.
html
.
exec
(
raw
)
if
(
result
[
2
])
{
if
(
result
[
2
])
{
return
{
return
{
...
yaml
.
safeLoad
(
result
[
2
]),
...
yaml
.
safeLoad
(
result
[
2
]),
...
@@ -210,6 +197,7 @@ export class Page extends Model {
...
@@ -210,6 +197,7 @@ export class Page extends Model {
}
}
break
break
}
}
}
}
catch
(
err
)
{
}
catch
(
err
)
{
WIKI
.
logger
.
warn
(
'Failed to parse page metadata. Invalid syntax.'
)
WIKI
.
logger
.
warn
(
'Failed to parse page metadata. Invalid syntax.'
)
}
}
...
...
server/package.json
View file @
291fe262
...
@@ -85,6 +85,7 @@
...
@@ -85,6 +85,7 @@
"graphql-rate-limit-directive"
:
"2.0.4"
,
"graphql-rate-limit-directive"
:
"2.0.4"
,
"graphql-tools"
:
"9.0.0"
,
"graphql-tools"
:
"9.0.0"
,
"graphql-upload"
:
"16.0.2"
,
"graphql-upload"
:
"16.0.2"
,
"gray-matter"
:
"4.0.3"
,
"he"
:
"1.2.0"
,
"he"
:
"1.2.0"
,
"highlight.js"
:
"11.9.0"
,
"highlight.js"
:
"11.9.0"
,
"image-size"
:
"1.0.2"
,
"image-size"
:
"1.0.2"
,
...
...
server/pnpm-lock.yaml
View file @
291fe262
...
@@ -152,6 +152,9 @@ dependencies:
...
@@ -152,6 +152,9 @@ dependencies:
graphql-upload
:
graphql-upload
:
specifier
:
16.0.2
specifier
:
16.0.2
version
:
16.0.2(graphql@16.8.1)
version
:
16.0.2(graphql@16.8.1)
gray-matter
:
specifier
:
4.0.3
version
:
4.0.3
he
:
he
:
specifier
:
1.2.0
specifier
:
1.2.0
version
:
1.2.0
version
:
1.2.0
...
@@ -2114,6 +2117,12 @@ packages:
...
@@ -2114,6 +2117,12 @@ packages:
resolution
:
{
integrity
:
sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==
}
resolution
:
{
integrity
:
sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==
}
dev
:
false
dev
:
false
/argparse@1.0.10
:
resolution
:
{
integrity
:
sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
}
dependencies
:
sprintf-js
:
1.0.3
dev
:
false
/argparse@2.0.1
:
/argparse@2.0.1
:
resolution
:
{
integrity
:
sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
}
resolution
:
{
integrity
:
sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
}
...
@@ -3852,6 +3861,13 @@ packages:
...
@@ -3852,6 +3861,13 @@ packages:
-
supports-color
-
supports-color
dev
:
false
dev
:
false
/extend-shallow@2.0.1
:
resolution
:
{
integrity
:
sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==
}
engines
:
{
node
:
'
>=0.10.0'
}
dependencies
:
is-extendable
:
0.1.1
dev
:
false
/extend@3.0.2
:
/extend@3.0.2
:
resolution
:
{
integrity
:
sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
}
resolution
:
{
integrity
:
sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
}
dev
:
false
dev
:
false
...
@@ -4290,6 +4306,16 @@ packages:
...
@@ -4290,6 +4306,16 @@ packages:
engines
:
{
node
:
^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0
}
engines
:
{
node
:
^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0
}
dev
:
false
dev
:
false
/gray-matter@4.0.3
:
resolution
:
{
integrity
:
sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==
}
engines
:
{
node
:
'
>=6.0'
}
dependencies
:
js-yaml
:
3.14.1
kind-of
:
6.0.3
section-matter
:
1.0.0
strip-bom-string
:
1.0.0
dev
:
false
/has-bigints@1.0.2
:
/has-bigints@1.0.2
:
resolution
:
{
integrity
:
sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==
}
resolution
:
{
integrity
:
sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==
}
dev
:
true
dev
:
true
...
@@ -4625,6 +4651,11 @@ packages:
...
@@ -4625,6 +4651,11 @@ packages:
has-tostringtag
:
1.0.0
has-tostringtag
:
1.0.0
dev
:
true
dev
:
true
/is-extendable@0.1.1
:
resolution
:
{
integrity
:
sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==
}
engines
:
{
node
:
'
>=0.10.0'
}
dev
:
false
/is-extglob@2.1.1
:
/is-extglob@2.1.1
:
resolution
:
{
integrity
:
sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
}
resolution
:
{
integrity
:
sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
}
engines
:
{
node
:
'
>=0.10.0'
}
engines
:
{
node
:
'
>=0.10.0'
}
...
@@ -4762,6 +4793,14 @@ packages:
...
@@ -4762,6 +4793,14 @@ packages:
dev
:
false
dev
:
false
optional
:
true
optional
:
true
/js-yaml@3.14.1
:
resolution
:
{
integrity
:
sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
}
hasBin
:
true
dependencies
:
argparse
:
1.0.10
esprima
:
4.0.1
dev
:
false
/js-yaml@4.1.0
:
/js-yaml@4.1.0
:
resolution
:
{
integrity
:
sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
}
resolution
:
{
integrity
:
sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
}
hasBin
:
true
hasBin
:
true
...
@@ -4895,6 +4934,11 @@ packages:
...
@@ -4895,6 +4934,11 @@ packages:
json-buffer
:
3.0.1
json-buffer
:
3.0.1
dev
:
true
dev
:
true
/kind-of@6.0.3
:
resolution
:
{
integrity
:
sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
}
engines
:
{
node
:
'
>=0.10.0'
}
dev
:
false
/klaw@4.1.0
:
/klaw@4.1.0
:
resolution
:
{
integrity
:
sha512-1zGZ9MF9H22UnkpVeuaGKOjfA2t6WrfdrJmGjy16ykcjnKQDmHVX+KI477rpbGevz/5FD4MC3xf1oxylBgcaQw==
}
resolution
:
{
integrity
:
sha512-1zGZ9MF9H22UnkpVeuaGKOjfA2t6WrfdrJmGjy16ykcjnKQDmHVX+KI477rpbGevz/5FD4MC3xf1oxylBgcaQw==
}
engines
:
{
node
:
'
>=14.14.0'
}
engines
:
{
node
:
'
>=14.14.0'
}
...
@@ -6591,6 +6635,14 @@ packages:
...
@@ -6591,6 +6635,14 @@ packages:
apg-lib
:
3.2.0
apg-lib
:
3.2.0
dev
:
false
dev
:
false
/section-matter@1.0.0
:
resolution
:
{
integrity
:
sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==
}
engines
:
{
node
:
'
>=4'
}
dependencies
:
extend-shallow
:
2.0.1
kind-of
:
6.0.3
dev
:
false
/semver@6.3.1
:
/semver@6.3.1
:
resolution
:
{
integrity
:
sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
}
resolution
:
{
integrity
:
sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
}
hasBin
:
true
hasBin
:
true
...
@@ -6821,6 +6873,10 @@ packages:
...
@@ -6821,6 +6873,10 @@ packages:
engines
:
{
node
:
'
>=
10.x'
}
engines
:
{
node
:
'
>=
10.x'
}
dev
:
false
dev
:
false
/sprintf-js@1.0.3
:
resolution
:
{
integrity
:
sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
}
dev
:
false
/statuses@2.0.1
:
/statuses@2.0.1
:
resolution
:
{
integrity
:
sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
}
resolution
:
{
integrity
:
sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
}
engines
:
{
node
:
'
>=
0.8'
}
engines
:
{
node
:
'
>=
0.8'
}
...
@@ -6890,6 +6946,11 @@ packages:
...
@@ -6890,6 +6946,11 @@ packages:
dependencies
:
dependencies
:
ansi-regex
:
5.0.1
ansi-regex
:
5.0.1
/strip-bom-string@1.0.0
:
resolution
:
{
integrity
:
sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==
}
engines
:
{
node
:
'
>=0.10.0'
}
dev
:
false
/strip-bom@3.0.0
:
/strip-bom@3.0.0
:
resolution
:
{
integrity
:
sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==
}
resolution
:
{
integrity
:
sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==
}
engines
:
{
node
:
'
>=4'
}
engines
:
{
node
:
'
>=4'
}
...
...
server/templates/demo/home.md
0 → 100644
View file @
291fe262
---
title
:
Home
description
:
Welcome to your wiki!
published
:
true
---
Feel free to modify this page (or delete it!).
## Next Steps
-
Configure your wiki in the
[
Administration Area
](
/_admin
)
.
-
[
Modify your profile
](
/_profile
)
to set preferences or change your password.
-
Create new pages by clicking the
<kbd>
+
</kbd>
button in the upper right corner.
-
Edit the navigation by clicking the
<kbd>
Edit Nav
</kbd>
button in the lower left corner.
## Read the documentation
How do permissions work? How can I make my wiki publicly accessible?
It's all
[
in the docs
](
https://beta.js.wiki/docs/admin/groups
)
!
## Example Blocks
Did you know that you can insert dynamic
[
blocks
](
https://beta.js.wiki/docs/editor/blocks
)
?
For example, here are the 5 most recently updated pages on your wiki:
::block-index{orderBy="updatedAt" orderByDirection="desc" limit="5"}
::
This list will automatically update as you create / edit pages.
ux/.eslintrc.js
View file @
291fe262
...
@@ -26,6 +26,8 @@ module.exports = {
...
@@ -26,6 +26,8 @@ module.exports = {
'plugin:vue/vue3-strongly-recommended'
,
// Priority B: Strongly Recommended (Improving Readability)
'plugin:vue/vue3-strongly-recommended'
,
// Priority B: Strongly Recommended (Improving Readability)
// 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
// 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
'plugin:vue-pug/vue3-strongly-recommended'
,
'standard'
'standard'
],
],
...
@@ -72,6 +74,14 @@ module.exports = {
...
@@ -72,6 +74,14 @@ module.exports = {
'vue/multi-word-component-names'
:
'off'
,
'vue/multi-word-component-names'
:
'off'
,
// allow debugger during development only
// allow debugger during development only
'no-debugger'
:
process
.
env
.
NODE_ENV
===
'production'
?
'error'
:
'off'
'no-debugger'
:
process
.
env
.
NODE_ENV
===
'production'
?
'error'
:
'off'
,
// disable bogus rules
'vue/valid-template-root'
:
'off'
,
'vue/no-parsing-error'
:
'off'
,
'vue-pug/no-parsing-error'
:
'off'
,
'vue/valid-v-for'
:
'off'
,
'vue/html-quotes'
:
[
'warn'
,
'single'
],
'vue/max-attributes-per-line'
:
'off'
}
}
}
}
ux/jsconfig.json
View file @
291fe262
...
@@ -40,7 +40,7 @@
...
@@ -40,7 +40,7 @@
"vueCompilerOptions"
:
{
"vueCompilerOptions"
:
{
"target"
:
3
,
"target"
:
3
,
"plugins"
:
[
"plugins"
:
[
"@v
olar/vue-
language-plugin-pug"
"@v
ue/
language-plugin-pug"
]
]
}
}
}
}
ux/package.json
View file @
291fe262
...
@@ -117,7 +117,8 @@
...
@@ -117,7 +117,8 @@
"eslint-plugin-import"
:
"2.29.0"
,
"eslint-plugin-import"
:
"2.29.0"
,
"eslint-plugin-n"
:
"16.3.1"
,
"eslint-plugin-n"
:
"16.3.1"
,
"eslint-plugin-promise"
:
"6.1.1"
,
"eslint-plugin-promise"
:
"6.1.1"
,
"eslint-plugin-vue"
:
"9.18.1"
"eslint-plugin-vue"
:
"9.18.1"
,
"eslint-plugin-vue-pug"
:
"0.6.1"
},
},
"engines"
:
{
"engines"
:
{
"node"
:
">= 18.0"
,
"node"
:
">= 18.0"
,
...
...
ux/pnpm-lock.yaml
View file @
291fe262
...
@@ -325,6 +325,9 @@ devDependencies:
...
@@ -325,6 +325,9 @@ devDependencies:
eslint-plugin-vue
:
eslint-plugin-vue
:
specifier
:
9.18.1
specifier
:
9.18.1
version
:
9.18.1(eslint@8.54.0)
version
:
9.18.1(eslint@8.54.0)
eslint-plugin-vue-pug
:
specifier
:
0.6.1
version
:
0.6.1(eslint-plugin-vue@9.18.1)(vue-eslint-parser@9.3.2)
packages
:
packages
:
...
@@ -2736,6 +2739,17 @@ packages:
...
@@ -2736,6 +2739,17 @@ packages:
eslint
:
8.54.0
eslint
:
8.54.0
dev
:
true
dev
:
true
/eslint-plugin-vue-pug@0.6.1(eslint-plugin-vue@9.18.1)(vue-eslint-parser@9.3.2)
:
resolution
:
{
integrity
:
sha512-wOId81xH42+X9M0qVRU5o39KJ3Phd+fdgemPNAy1cD9hUROp/aSHHapOP7muDV/sHmu9zP/mU34yDfCfQWUWEQ==
}
peerDependencies
:
eslint-plugin-vue
:
^9.8.0
dependencies
:
eslint-plugin-vue
:
9.18.1(eslint@8.54.0)
vue-eslint-parser-template-tokenizer-pug
:
0.4.10(vue-eslint-parser@9.3.2)
transitivePeerDependencies
:
-
vue-eslint-parser
dev
:
true
/eslint-plugin-vue@9.18.1(eslint@8.54.0)
:
/eslint-plugin-vue@9.18.1(eslint@8.54.0)
:
resolution
:
{
integrity
:
sha512-7hZFlrEgg9NIzuVik2I9xSnJA5RsmOfueYgsUGUokEDLJ1LHtxO0Pl4duje1BriZ/jDWb+44tcIlC3yi0tdlZg==
}
resolution
:
{
integrity
:
sha512-7hZFlrEgg9NIzuVik2I9xSnJA5RsmOfueYgsUGUokEDLJ1LHtxO0Pl4duje1BriZ/jDWb+44tcIlC3yi0tdlZg==
}
engines
:
{
node
:
^14.17.0 || >=16.0.0
}
engines
:
{
node
:
^14.17.0 || >=16.0.0
}
...
@@ -5478,6 +5492,15 @@ packages:
...
@@ -5478,6 +5492,15 @@ packages:
dependencies
:
dependencies
:
vue
:
3.3.8(typescript@5.3.2)
vue
:
3.3.8(typescript@5.3.2)
/vue-eslint-parser-template-tokenizer-pug@0.4.10(vue-eslint-parser@9.3.2)
:
resolution
:
{
integrity
:
sha512-Npzjna9PUJzIal/o7hOo4D7dF4hqjHwTafBtLgdtja2LZuCc4UT5BU7dyYJeKb9s1SnCFBflHMg3eFA3odq6bg==
}
peerDependencies
:
vue-eslint-parser
:
^9.0.0
dependencies
:
pug-lexer
:
5.0.1
vue-eslint-parser
:
9.3.2(eslint@8.54.0)
dev
:
true
/vue-eslint-parser@9.3.2(eslint@8.54.0)
:
/vue-eslint-parser@9.3.2(eslint@8.54.0)
:
resolution
:
{
integrity
:
sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==
}
resolution
:
{
integrity
:
sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==
}
engines
:
{
node
:
^14.17.0 || >=16.0.0
}
engines
:
{
node
:
^14.17.0 || >=16.0.0
}
...
...
ux/public/_assets/icons/ultraviolet-image.svg
0 → 100644
View file @
291fe262
<svg
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 40 40"
width=
"80px"
height=
"80px"
><path
fill=
"#dff0fe"
d=
"M1.5 4.5H38.5V35.5H1.5z"
/><path
fill=
"#4788c7"
d=
"M38,5v30H2V5H38 M39,4H1v32h38V4L39,4z"
/><path
fill=
"#98ccfd"
d=
"M27.45 19.612L20 25.437 30.247 35 38 35 38 29.112zM30 10A3 3 0 1 0 30 16 3 3 0 1 0 30 10z"
/><path
fill=
"#b6dcfe"
d=
"M32.468 35L2 35 2 27.421 14 17.316z"
/><g><path
fill=
"#fff"
d=
"M36,7v26H4V7H36 M38,5H2v30h36V5L38,5z"
/></g></svg>
\ No newline at end of file
ux/src/components/ApiKeyCopyDialog.vue
View file @
291fe262
...
@@ -5,7 +5,7 @@ q-dialog(ref='dialogRef', @hide='onDialogHide', persistent)
...
@@ -5,7 +5,7 @@ q-dialog(ref='dialogRef', @hide='onDialogHide', persistent)
q-icon(name='img:/_assets/icons/fluent-key-2.svg', left, size='sm')
q-icon(name='img:/_assets/icons/fluent-key-2.svg', left, size='sm')
span
{{
t
(
`admin.api.copyKeyTitle`
)
}}
span
{{
t
(
`admin.api.copyKeyTitle`
)
}}
q-card-section.card-negative
q-card-section.card-negative
i18n-t(tag='span', keypath='admin.api.newKeyCopyWarn')
i18n-t(tag='span', keypath='admin.api.newKeyCopyWarn'
, scope='global'
)
template(#bold)
template(#bold)
strong
{{
t
(
'admin.api.newKeyCopyWarnBold'
)
}}
strong
{{
t
(
'admin.api.newKeyCopyWarnBold'
)
}}
q-form.q-py-sm
q-form.q-py-sm
...
@@ -15,7 +15,7 @@ q-dialog(ref='dialogRef', @hide='onDialogHide', persistent)
...
@@ -15,7 +15,7 @@ q-dialog(ref='dialogRef', @hide='onDialogHide', persistent)
q-input(
q-input(
type='textarea'
type='textarea'
outlined
outlined
v-model
='props.keyValue'
:model-value
='props.keyValue'
dense
dense
hide-bottom-space
hide-bottom-space
:label='t(`admin.api.key`)'
:label='t(`admin.api.key`)'
...
...
ux/src/components/ApiKeyCreateDialog.vue
View file @
291fe262
...
@@ -64,11 +64,11 @@ q-dialog(ref='dialogRef', @hide='onDialogHide')
...
@@ -64,11 +64,11 @@ q-dialog(ref='dialogRef', @hide='onDialogHide')
)
)
template(v-slot:selected)
template(v-slot:selected)
.text-caption(v-if='state.keyGroups.length > 1')
.text-caption(v-if='state.keyGroups.length > 1')
i18n-t(keypath='admin.api.groupsSelected')
i18n-t(keypath='admin.api.groupsSelected'
, scope='global'
)
template(#count)
template(#count)
strong
{{
state
.
keyGroups
.
length
}}
strong
{{
state
.
keyGroups
.
length
}}
.text-caption(v-else-if='state.keyGroups.length === 1')
.text-caption(v-else-if='state.keyGroups.length === 1')
i18n-t(keypath='admin.api.groupSelected')
i18n-t(keypath='admin.api.groupSelected'
, scope='global'
)
template(#group)
template(#group)
strong
{{
selectedGroupName
}}
strong
{{
selectedGroupName
}}
span(v-else)
span(v-else)
...
...
ux/src/components/AssetDeleteDialog.vue
0 → 100644
View file @
291fe262
<
template
lang=
'pug'
>
q-dialog(ref='dialogRef', @hide='onDialogHide')
q-card(style='min-width: 550px; max-width: 850px;')
q-card-section.card-header
q-icon(name='img:/_assets/icons/fluent-delete-bin.svg', left, size='sm')
span
{{
t
(
`fileman.assetDelete`
)
}}
q-card-section
.text-body2
i18n-t(keypath='fileman.assetDeleteConfirm')
template(#name)
strong
{{
assetName
}}
.text-caption.text-grey.q-mt-sm
{{
t
(
'fileman.assetDeleteId'
,
{
id
:
assetId
}
)
}}
q
-
card
-
actions
.
card
-
actions
q
-
space
q
-
btn
.
acrylic
-
btn
(
flat
:
label
=
't(`common.actions.cancel`)'
color
=
'grey'
padding
=
'xs md'
@
click
=
'onDialogCancel'
)
q
-
btn
(
unelevated
:
label
=
't(`common.actions.delete`)'
color
=
'negative'
padding
=
'xs md'
@
click
=
'confirm'
:
loading
=
'state.isLoading'
)
<
/template
>
<
script
setup
>
import
gql
from
'graphql-tag'
import
{
useI18n
}
from
'vue-i18n'
import
{
useDialogPluginComponent
,
useQuasar
}
from
'quasar'
import
{
reactive
}
from
'vue'
// PROPS
const
props
=
defineProps
({
assetId
:
{
type
:
String
,
required
:
true
}
,
assetName
:
{
type
:
String
,
required
:
true
}
}
)
// EMITS
defineEmits
([
...
useDialogPluginComponent
.
emits
])
// QUASAR
const
{
dialogRef
,
onDialogHide
,
onDialogOK
,
onDialogCancel
}
=
useDialogPluginComponent
()
const
$q
=
useQuasar
()
// I18N
const
{
t
}
=
useI18n
()
// DATA
const
state
=
reactive
({
isLoading
:
false
}
)
// METHODS
async
function
confirm
()
{
state
.
isLoading
=
true
try
{
const
resp
=
await
APOLLO_CLIENT
.
mutate
({
mutation
:
gql
`
mutation deleteAsset ($id: UUID!) {
deleteAsset(id: $id) {
operation {
succeeded
message
}
}
}
`
,
variables
:
{
id
:
props
.
assetId
}
}
)
if
(
resp
?.
data
?.
deleteAsset
?.
operation
?.
succeeded
)
{
$q
.
notify
({
type
:
'positive'
,
message
:
t
(
'fileman.assetDeleteSuccess'
)
}
)
onDialogOK
()
}
else
{
throw
new
Error
(
resp
?.
data
?.
deleteAsset
?.
operation
?.
message
||
'An unexpected error occured.'
)
}
}
catch
(
err
)
{
$q
.
notify
({
type
:
'negative'
,
message
:
err
.
message
}
)
}
state
.
isLoading
=
false
}
<
/script
>
ux/src/components/AssetRenameDialog.vue
0 → 100644
View file @
291fe262
<
template
lang=
'pug'
>
q-dialog(ref='dialogRef', @hide='onDialogHide')
q-card(style='min-width: 650px;')
q-card-section.card-header
q-icon(name='img:/_assets/icons/fluent-rename.svg', left, size='sm')
span
{{
t
(
`fileman.assetRename`
)
}}
q-form.q-py-sm(@submit='rename')
q-item
blueprint-icon.self-start(icon='image')
q-item-section
q-input(
autofocus
outlined
v-model='state.path'
dense
hide-bottom-space
:label='t(`fileman.assetFileName`)'
:aria-label='t(`fileman.assetFileName`)'
:hint='t(`fileman.assetFileNameHint`)'
lazy-rules='ondemand'
@keyup.enter='rename'
)
q-card-actions.card-actions
q-space
q-btn.acrylic-btn(
flat
:label='t(`common.actions.cancel`)'
color='grey'
padding='xs md'
@click='onDialogCancel'
)
q-btn(
unelevated
:label='t(`common.actions.rename`)'
color='primary'
padding='xs md'
@click='rename'
:loading='state.loading > 0'
)
q-inner-loading(:showing='state.loading > 0')
q-spinner(color='accent', size='lg')
</
template
>
<
script
setup
>
import
gql
from
'graphql-tag'
import
{
useI18n
}
from
'vue-i18n'
import
{
useDialogPluginComponent
,
useQuasar
}
from
'quasar'
import
{
onMounted
,
reactive
,
ref
}
from
'vue'
// PROPS
const
props
=
defineProps
({
assetId
:
{
type
:
String
,
required
:
true
}
})
// EMITS
defineEmits
([
...
useDialogPluginComponent
.
emits
])
// QUASAR
const
{
dialogRef
,
onDialogHide
,
onDialogOK
,
onDialogCancel
}
=
useDialogPluginComponent
()
const
$q
=
useQuasar
()
// I18N
const
{
t
}
=
useI18n
()
// DATA
const
state
=
reactive
({
path
:
''
,
loading
:
false
})
// METHODS
async
function
rename
()
{
state
.
loading
++
try
{
if
(
state
.
path
?.
length
<
2
||
!
state
.
path
?.
includes
(
'.'
))
{
throw
new
Error
(
t
(
'fileman.renameAssetInvalid'
))
}
const
resp
=
await
APOLLO_CLIENT
.
mutate
({
mutation
:
gql
`
mutation renameAsset (
$id: UUID!
$fileName: String!
) {
renameAsset (
id: $id
fileName: $fileName
) {
operation {
succeeded
message
}
}
}
`
,
variables
:
{
id
:
props
.
assetId
,
fileName
:
state
.
path
}
})
if
(
resp
?.
data
?.
renameAsset
?.
operation
?.
succeeded
)
{
$q
.
notify
({
type
:
'positive'
,
message
:
t
(
'fileman.renameAssetSuccess'
)
})
onDialogOK
()
}
else
{
throw
new
Error
(
resp
?.
data
?.
renameAsset
?.
operation
?.
message
||
'An unexpected error occured.'
)
}
}
catch
(
err
)
{
$q
.
notify
({
type
:
'negative'
,
message
:
err
.
message
})
}
state
.
loading
--
}
// MOUNTED
onMounted
(
async
()
=>
{
state
.
loading
++
try
{
const
resp
=
await
APOLLO_CLIENT
.
query
({
query
:
gql
`
query fetchAssetForRename (
$id: UUID!
) {
assetById (
id: $id
) {
id
fileName
}
}
`
,
fetchPolicy
:
'network-only'
,
variables
:
{
id
:
props
.
assetId
}
})
if
(
resp
?.
data
?.
assetById
?.
id
!==
props
.
assetId
)
{
throw
new
Error
(
'Failed to fetch asset data.'
)
}
state
.
path
=
resp
.
data
.
assetById
.
fileName
}
catch
(
err
)
{
$q
.
notify
({
type
:
'negative'
,
message
:
err
.
message
})
onDialogCancel
()
}
state
.
loading
--
})
</
script
>
ux/src/components/FileManager.vue
View file @
291fe262
<
template
lang=
"pug"
>
<
template
lang=
'pug'
>
q-layout.fileman(view='hHh lpR lFr', container)
q-layout.fileman(view='hHh lpR lFr', container)
q-header.card-header
q-header.card-header
q-toolbar(dark)
q-toolbar(dark)
q-icon(name='img:/_assets/icons/fluent-folder.svg', left, size='md')
q-icon(name='img:/_assets/icons/fluent-folder.svg', left, size='md')
span
{{
t
(
`fileman.title`
)
}}
span
{{
t
(
`fileman.title`
)
}}
q-toolbar(dark)
q-toolbar(dark)
q-btn.q-mr-sm.acrylic-btn(
q-btn.q-mr-sm.acrylic-btn(
flat
flat
...
@@ -23,9 +23,9 @@ q-layout.fileman(view='hHh lpR lFr', container)
...
@@ -23,9 +23,9 @@ q-layout.fileman(view='hHh lpR lFr', container)
:label='t(`fileman.searchFolder`)'
:label='t(`fileman.searchFolder`)'
:debounce='500'
:debounce='500'
)
)
template(
v-slot:
prepend)
template(
#
prepend)
q-icon(name='las la-search')
q-icon(name='las la-search')
template(
v-slot:
append)
template(
#
append)
q-icon.cursor-pointer(
q-icon.cursor-pointer(
name='las la-times'
name='las la-times'
@click='state.search=``'
@click='state.search=``'
...
@@ -78,9 +78,10 @@ q-layout.fileman(view='hHh lpR lFr', container)
...
@@ -78,9 +78,10 @@ q-layout.fileman(view='hHh lpR lFr', container)
)
)
.fileman-details-row(
.fileman-details-row(
v-for='item of currentFileDetails.items'
v-for='item of currentFileDetails.items'
:key='item.id'
)
)
label
{{
item
.
label
}}
label
{{
item
.
label
}}
span
{{
item
.
value
}}
span
{{
item
.
value
}}
template(v-if='insertMode')
template(v-if='insertMode')
q-separator.q-my-md
q-separator.q-my-md
q-btn.full-width(
q-btn.full-width(
...
@@ -97,7 +98,7 @@ q-layout.fileman(view='hHh lpR lFr', container)
...
@@ -97,7 +98,7 @@ q-layout.fileman(view='hHh lpR lFr', container)
q-toolbar.fileman-toolbar
q-toolbar.fileman-toolbar
template(v-if='state.isUploading')
template(v-if='state.isUploading')
.fileman-progressbar
.fileman-progressbar
div(:style='`width: ` + state.uploadPercentage + `%`')
{{
state
.
uploadPercentage
}}
%
div(:style='`width: ` + state.uploadPercentage + `%`')
{{
state
.
uploadPercentage
}}
%
q-btn.acrylic-btn.q-ml-sm(
q-btn.acrylic-btn.q-ml-sm(
flat
flat
dense
dense
...
@@ -117,9 +118,8 @@ q-layout.fileman(view='hHh lpR lFr', container)
...
@@ -117,9 +118,8 @@ q-layout.fileman(view='hHh lpR lFr', container)
color='grey'
color='grey'
:aria-label='t(`fileman.viewOptions`)'
:aria-label='t(`fileman.viewOptions`)'
icon='las la-th-list'
icon='las la-th-list'
@click=''
)
)
q-tooltip(anchor='bottom middle', self='top middle')
{{
t
(
`fileman.viewOptions`
)
}}
q-tooltip(anchor='bottom middle', self='top middle')
{{
t
(
`fileman.viewOptions`
)
}}
q-menu(
q-menu(
transition-show='jump-down'
transition-show='jump-down'
transition-hide='jump-up'
transition-hide='jump-up'
...
@@ -128,7 +128,7 @@ q-layout.fileman(view='hHh lpR lFr', container)
...
@@ -128,7 +128,7 @@ q-layout.fileman(view='hHh lpR lFr', container)
)
)
q-card.q-pa-sm
q-card.q-pa-sm
.text-center
.text-center
small.text-grey
{{
t
(
`fileman.viewOptions`
)
}}
small.text-grey
{{
t
(
`fileman.viewOptions`
)
}}
q-list(dense)
q-list(dense)
q-separator.q-my-sm
q-separator.q-my-sm
q-item(clickable)
q-item(clickable)
...
@@ -183,7 +183,7 @@ q-layout.fileman(view='hHh lpR lFr', container)
...
@@ -183,7 +183,7 @@ q-layout.fileman(view='hHh lpR lFr', container)
icon='las la-redo-alt'
icon='las la-redo-alt'
@click='reloadFolder(state.currentFolderId)'
@click='reloadFolder(state.currentFolderId)'
)
)
q-tooltip(anchor='bottom middle', self='top middle')
{{
t
(
`common.actions.refresh`
)
}}
q-tooltip(anchor='bottom middle', self='top middle')
{{
t
(
`common.actions.refresh`
)
}}
q-separator.q-mr-sm(inset, vertical)
q-separator.q-mr-sm(inset, vertical)
q-btn.q-mr-sm(
q-btn.q-mr-sm(
flat
flat
...
@@ -193,7 +193,6 @@ q-layout.fileman(view='hHh lpR lFr', container)
...
@@ -193,7 +193,6 @@ q-layout.fileman(view='hHh lpR lFr', container)
:label='t(`common.actions.new`)'
:label='t(`common.actions.new`)'
:aria-label='t(`common.actions.new`)'
:aria-label='t(`common.actions.new`)'
icon='las la-plus-circle'
icon='las la-plus-circle'
@click=''
)
)
new-menu(
new-menu(
:hide-asset-btn='true'
:hide-asset-btn='true'
...
@@ -236,16 +235,16 @@ q-layout.fileman(view='hHh lpR lFr', container)
...
@@ -236,16 +235,16 @@ q-layout.fileman(view='hHh lpR lFr', container)
clickable
clickable
active-class='active'
active-class='active'
:active='item.id === state.currentFileId'
:active='item.id === state.currentFileId'
@click
.native
='selectItem(item)'
@click='selectItem(item)'
@dblclick
.native
='doubleClickItem(item)'
@dblclick='doubleClickItem(item)'
)
)
q-item-section.fileman-filelist-icon(avatar)
q-item-section.fileman-filelist-icon(avatar)
q-icon(:name='item.icon', :size='state.isCompact ? `md` : `xl`')
q-icon(:name='item.icon', :size='state.isCompact ? `md` : `xl`')
q-item-section.fileman-filelist-label
q-item-section.fileman-filelist-label
q-item-label
{{
usePathTitle
?
item
.
fileName
:
item
.
title
}}
q-item-label
{{
usePathTitle
?
item
.
fileName
:
item
.
title
}}
q-item-label(caption, v-if='!state.isCompact')
{{
item
.
caption
}}
q-item-label(caption, v-if='!state.isCompact')
{{
item
.
caption
}}
q-item-section.fileman-filelist-side(side, v-if='item.side')
q-item-section.fileman-filelist-side(side, v-if='item.side')
.text-caption
{{
item
.
side
}}
.text-caption
{{
item
.
side
}}
//- RIGHT-CLICK MENU
//- RIGHT-CLICK MENU
q-menu.translucent-menu(
q-menu.translucent-menu(
touch-position
touch-position
...
@@ -341,6 +340,7 @@ import { useSiteStore } from 'src/stores/site'
...
@@ -341,6 +340,7 @@ import { useSiteStore } from 'src/stores/site'
import
FolderCreateDialog
from
'src/components/FolderCreateDialog.vue'
import
FolderCreateDialog
from
'src/components/FolderCreateDialog.vue'
import
FolderDeleteDialog
from
'src/components/FolderDeleteDialog.vue'
import
FolderDeleteDialog
from
'src/components/FolderDeleteDialog.vue'
import
FolderRenameDialog
from
'src/components/FolderRenameDialog.vue'
import
FolderRenameDialog
from
'src/components/FolderRenameDialog.vue'
import
AssetRenameDialog
from
'src/components/AssetRenameDialog.vue'
import
LocaleSelectorMenu
from
'src/components/LocaleSelectorMenu.vue'
import
LocaleSelectorMenu
from
'src/components/LocaleSelectorMenu.vue'
// QUASAR
// QUASAR
...
@@ -787,6 +787,7 @@ function reloadFolder (folderId) {
...
@@ -787,6 +787,7 @@ function reloadFolder (folderId) {
treeComp
.
value
.
resetLoaded
()
treeComp
.
value
.
resetLoaded
()
}
}
// --------------------------------------
// PAGE METHODS
// PAGE METHODS
// --------------------------------------
// --------------------------------------
...
@@ -812,6 +813,34 @@ function delPage (pageId, pageName) {
...
@@ -812,6 +813,34 @@ function delPage (pageId, pageName) {
}
}
// --------------------------------------
// --------------------------------------
// ASSET METHODS
// --------------------------------------
function
renameAsset
(
assetId
)
{
$q
.
dialog
({
component
:
AssetRenameDialog
,
componentProps
:
{
assetId
}
}).
onOk
(
async
()
=>
{
// -> Reload current view
await
loadTree
({
parentId
:
state
.
currentFolderId
})
})
}
function
delAsset
(
assetId
,
assetName
)
{
$q
.
dialog
({
component
:
defineAsyncComponent
(()
=>
import
(
'src/components/AssetDeleteDialog.vue'
)),
componentProps
:
{
assetId
,
assetName
}
}).
onOk
(()
=>
{
loadTree
(
state
.
currentFolderId
,
null
)
})
}
// --------------------------------------
// UPLOAD METHODS
// UPLOAD METHODS
// --------------------------------------
// --------------------------------------
...
@@ -991,7 +1020,7 @@ function renameItem (item) {
...
@@ -991,7 +1020,7 @@ function renameItem (item) {
break
break
}
}
case
'asset'
:
{
case
'asset'
:
{
// TODO: Rename asset
renameAsset
(
item
.
id
)
break
break
}
}
}
}
...
@@ -999,6 +1028,10 @@ function renameItem (item) {
...
@@ -999,6 +1028,10 @@ function renameItem (item) {
function
delItem
(
item
)
{
function
delItem
(
item
)
{
switch
(
item
.
type
)
{
switch
(
item
.
type
)
{
case
'asset'
:
{
delAsset
(
item
.
id
,
item
.
title
)
break
}
case
'folder'
:
{
case
'folder'
:
{
delFolder
(
item
.
id
,
true
)
delFolder
(
item
.
id
,
true
)
break
break
...
...
ux/src/components/FooterNav.vue
View file @
291fe262
...
@@ -5,6 +5,7 @@ q-footer.site-footer
...
@@ -5,6 +5,7 @@ q-footer.site-footer
v-if='hasSiteFooter'
v-if='hasSiteFooter'
:keypath='isCopyright ? `common.footerCopyright` : `common.footerLicense`'
:keypath='isCopyright ? `common.footerCopyright` : `common.footerLicense`'
tag='span'
tag='span'
scope='global'
)
)
template(#company)
template(#company)
strong
{{
siteStore
.
company
}}
strong
{{
siteStore
.
company
}}
...
@@ -15,6 +16,7 @@ q-footer.site-footer
...
@@ -15,6 +16,7 @@ q-footer.site-footer
i18n-t(
i18n-t(
:keypath='props.generic ? `common.footerGeneric` : `common.footerPoweredBy`'
:keypath='props.generic ? `common.footerGeneric` : `common.footerPoweredBy`'
tag='span'
tag='span'
scope='global'
)
)
template(#link)
template(#link)
a(href='https://js.wiki', target='_blank', ref='noopener noreferrer'): strong Wiki.js
a(href='https://js.wiki', target='_blank', ref='noopener noreferrer'): strong Wiki.js
...
...
ux/src/components/HeaderSearch.vue
View file @
291fe262
...
@@ -66,6 +66,7 @@ q-toolbar(
...
@@ -66,6 +66,7 @@ q-toolbar(
.flex.q-mb-md
.flex.q-mb-md
q-chip(
q-chip(
v-for='tag of popularTags'
v-for='tag of popularTags'
:key='tag'
square
square
color='grey-8'
color='grey-8'
text-color='white'
text-color='white'
...
...
ux/src/components/IconPickerDialog.vue
View file @
291fe262
<!-- eslint-disable -->
<
template
lang=
"pug"
>
<
template
lang=
"pug"
>
q-card.icon-picker(flat, style='width: 400px
;
')
q-card.icon-picker(flat, style='width: 400px')
q-tabs.text-primary(
q-tabs.text-primary(
v-model='state.currentTab'
v-model='state.currentTab'
no-caps
no-caps
...
@@ -43,7 +44,8 @@ q-card.icon-picker(flat, style='width: 400px;')
...
@@ -43,7 +44,8 @@ q-card.icon-picker(flat, style='width: 400px;')
)
)
q-item-section
q-item-section
q-item-label
{{
scope
.
opt
.
name
}}
q-item-label
{{
scope
.
opt
.
name
}}
q-item-label(caption): strong(:class='scope.selected ? `text-white` : `text-primary`')
{{
scope
.
opt
.
subset
}}
q-item-label(caption)
strong(:class='scope.selected ? `text-white` : `text-primary`')
{{
scope
.
opt
.
subset
}}
q-item-section(side, v-if='scope.opt.subset')
q-item-section(side, v-if='scope.opt.subset')
q-chip(
q-chip(
color='primary'
color='primary'
...
...
ux/src/components/LocaleSelectorMenu.vue
View file @
291fe262
...
@@ -8,6 +8,7 @@ q-menu.translucent-menu(
...
@@ -8,6 +8,7 @@ q-menu.translucent-menu(
q-list(padding, style='min-width: 200px;')
q-list(padding, style='min-width: 200px;')
q-item(
q-item(
v-for='lang of siteStore.locales.active'
v-for='lang of siteStore.locales.active'
:key='lang.code'
clickable
clickable
@click='commonStore.setLocale(lang.code)'
@click='commonStore.setLocale(lang.code)'
)
)
...
...
ux/src/components/NavSidebar.vue
View file @
291fe262
...
@@ -27,6 +27,7 @@ q-scroll-area.sidebar-nav(
...
@@ -27,6 +27,7 @@ q-scroll-area.sidebar-nav(
q-item(
q-item(
v-for='itemChild of item.children'
v-for='itemChild of item.children'
:to='itemChild.target'
:to='itemChild.target'
:key='itemChild.id'
)
)
q-item-section(side)
q-item-section(side)
q-icon(:name='itemChild.icon', color='white')
q-icon(:name='itemChild.icon', color='white')
...
...
ux/src/components/PageActionsCol.vue
View file @
291fe262
...
@@ -48,7 +48,10 @@
...
@@ -48,7 +48,10 @@
span Pending Asset Uploads
span Pending Asset Uploads
q-card-section(v-if='!hasPendingAssets') There are no assets pending uploads.
q-card-section(v-if='!hasPendingAssets') There are no assets pending uploads.
q-list(v-else, separator)
q-list(v-else, separator)
q-item(v-for='item of editorStore.pendingAssets')
q-item(
v-for='item of editorStore.pendingAssets'
:key='item.id'
)
q-item-section(side)
q-item-section(side)
q-icon(name='las la-file-image')
q-icon(name='las la-file-image')
q-item-section
{{
item
.
fileName
}}
q-item-section
{{
item
.
fileName
}}
...
...
ux/src/components/PageTags.vue
View file @
291fe262
...
@@ -38,7 +38,8 @@
...
@@ -38,7 +38,8 @@
q-item-section(side)
q-item-section(side)
q-checkbox(:model-value='scope.selected', @update:model-value='scope.toggleOption(scope.opt)', size='sm')
q-checkbox(:model-value='scope.selected', @update:model-value='scope.toggleOption(scope.opt)', size='sm')
q-item-section
q-item-section
q-item-label(v-html='scope.opt')
q-item-label
span(v-html='scope.opt')
</
template
>
</
template
>
<
script
setup
>
<
script
setup
>
...
...
ux/src/components/SocialSharingMenu.vue
View file @
291fe262
...
@@ -7,7 +7,7 @@ q-menu(
...
@@ -7,7 +7,7 @@ q-menu(
@before-hide='menuHidden'
@before-hide='menuHidden'
)
)
q-list(dense, padding)
q-list(dense, padding)
q-item(clickable,
@click='',
ref='copyUrlButton')
q-item(clickable, ref='copyUrlButton')
q-item-section.items-center(avatar)
q-item-section.items-center(avatar)
q-icon(color='grey', name='las la-clipboard', size='sm')
q-icon(color='grey', name='las la-clipboard', size='sm')
q-item-section.q-pr-md Copy URL
q-item-section.q-pr-md Copy URL
...
...
ux/src/components/TreeBrowserDialog.vue
View file @
291fe262
<
template
lang=
"pug"
>
<
template
lang=
'pug'
>
q-dialog(ref='dialogRef', @hide='onDialogHide')
q-dialog(ref='dialogRef', @hide='onDialogHide')
q-card.page-save-dialog(style='width: 860px; max-width: 90vw;')
q-card.page-save-dialog(style='width: 860px; max-width: 90vw;')
q-card-section.card-header(v-if='props.mode === `savePage`')
q-card-section.card-header(v-if='props.mode === `savePage`')
q-icon(name='img:/_assets/icons/fluent-save-as.svg', left, size='sm')
q-icon(name='img:/_assets/icons/fluent-save-as.svg', left, size='sm')
span
{{
t
(
'pageSaveDialog.title'
)
}}
span
{{
t
(
'pageSaveDialog.title'
)
}}
q-card-section.card-header(v-else-if='props.mode === `duplicatePage`')
q-card-section.card-header(v-else-if='props.mode === `duplicatePage`')
q-icon(name='img:/_assets/icons/color-documents.svg', left, size='sm')
q-icon(name='img:/_assets/icons/color-documents.svg', left, size='sm')
span
{{
t
(
'pageDuplicateDialog.title'
)
}}
span
{{
t
(
'pageDuplicateDialog.title'
)
}}
q-card-section.card-header(v-else-if='props.mode === `renamePage`')
q-card-section.card-header(v-else-if='props.mode === `renamePage`')
q-icon(name='img:/_assets/icons/fluent-rename.svg', left, size='sm')
q-icon(name='img:/_assets/icons/fluent-rename.svg', left, size='sm')
span
{{
t
(
'pageRenameDialog.title'
)
}}
span
{{
t
(
'pageRenameDialog.title'
)
}}
.row.page-save-dialog-browser
.row.page-save-dialog-browser
.col-4
.col-4
q-scroll-area(
q-scroll-area(
...
@@ -36,13 +36,13 @@ q-dialog(ref='dialogRef', @hide='onDialogHide')
...
@@ -36,13 +36,13 @@ q-dialog(ref='dialogRef', @hide='onDialogHide')
clickable
clickable
active-class='active'
active-class='active'
:active='item.id === state.currentFileId'
:active='item.id === state.currentFileId'
@click
.native
='selectItem(item)'
@click='selectItem(item)'
)
)
q-item-section(side)
q-item-section(side)
q-icon(:name='item.icon', size='sm')
q-icon(:name='item.icon', size='sm')
q-item-section
q-item-section
q-item-label
{{
item
.
title
}}
q-item-label
{{
item
.
title
}}
.page-save-dialog-path.font-robotomono
{{
currentFolderPath
}}
.page-save-dialog-path.font-robotomono
{{
currentFolderPath
}}
q-list.q-py-sm
q-list.q-py-sm
q-item
q-item
blueprint-icon(icon='new-document')
blueprint-icon(icon='new-document')
...
...
ux/src/layouts/AdminLayout.vue
View file @
291fe262
...
@@ -28,6 +28,7 @@ q-layout.admin(view='hHh Lpr lff')
...
@@ -28,6 +28,7 @@ q-layout.admin(view='hHh Lpr lff')
q-list(separator, padding)
q-list(separator, padding)
q-item(
q-item(
v-for='lang of adminStore.locales'
v-for='lang of adminStore.locales'
:key='lang.code'
clickable
clickable
@click='commonStore.setLocale(lang.code)'
@click='commonStore.setLocale(lang.code)'
)
)
...
...
ux/src/layouts/MainLayout.vue
View file @
291fe262
...
@@ -13,7 +13,6 @@ q-layout(view='hHh Lpr lff')
...
@@ -13,7 +13,6 @@ q-layout(view='hHh Lpr lff')
icon='las la-globe'
icon='las la-globe'
color='white'
color='white'
aria-label='Switch Locale'
aria-label='Switch Locale'
@click=''
)
)
locale-selector-menu(anchor='top right' self='top left')
locale-selector-menu(anchor='top right' self='top left')
q-tooltip(anchor='center right' self='center left') Switch Locale
q-tooltip(anchor='center right' self='center left') Switch Locale
...
...
ux/src/pages/AdminExtensions.vue
View file @
291fe262
...
@@ -32,7 +32,7 @@ q-page.admin-extensions
...
@@ -32,7 +32,7 @@ q-page.admin-extensions
q-card
q-card
q-list(separator)
q-list(separator)
q-item(
q-item(
v-for='
(ext, idx)
of state.extensions'
v-for='
ext
of state.extensions'
:key='`ext-` + ext.key'
:key='`ext-` + ext.key'
)
)
blueprint-icon(icon='module')
blueprint-icon(icon='module')
...
...
ux/src/pages/AdminFlags.vue
View file @
291fe262
...
@@ -101,7 +101,6 @@ q-page.admin-flags
...
@@ -101,7 +101,6 @@ q-page.admin-flags
icon='las la-code'
icon='las la-code'
color='primary'
color='primary'
text-color='white'
text-color='white'
@click=''
disabled
disabled
)
)
...
...
ux/src/pages/AdminLocale.vue
View file @
291fe262
...
@@ -84,7 +84,7 @@ q-page.admin-locale
...
@@ -84,7 +84,7 @@ q-page.admin-locale
.text-caption(:class='$q.dark.isActive ? `text-grey-4` : `text-grey-7`') Select the locales that can be used on this site.
.text-caption(:class='$q.dark.isActive ? `text-grey-4` : `text-grey-7`') Select the locales that can be used on this site.
q-item(
q-item(
v-for='
(lc, idx)
of state.locales'
v-for='
lc
of state.locales'
:key='lc.code'
:key='lc.code'
:tag='lc.code !== state.selectedLocale ? `label` : null'
:tag='lc.code !== state.selectedLocale ? `label` : null'
)
)
...
...
ux/src/pages/AdminLogin.vue
View file @
291fe262
...
@@ -177,7 +177,6 @@ q-page.admin-login
...
@@ -177,7 +177,6 @@ q-page.admin-login
q-card-section.col-auto.q-pr-none
q-card-section.col-auto.q-pr-none
q-icon(name='las la-info-circle', size='sm')
q-icon(name='las la-info-circle', size='sm')
q-card-section.text-caption
{{
t
(
'admin.login.providersVisbleWarning'
)
}}
q-card-section.text-caption
{{
t
(
'admin.login.providersVisbleWarning'
)
}}
</
template
>
</
template
>
<
script
setup
>
<
script
setup
>
...
...
ux/src/pages/AdminMetrics.vue
View file @
291fe262
...
@@ -54,7 +54,7 @@ q-page.admin-api
...
@@ -54,7 +54,7 @@ q-page.admin-api
q-card-section.col-auto.q-pr-none
q-card-section.col-auto.q-pr-none
q-icon(name='las la-info-circle', size='sm')
q-icon(name='las la-info-circle', size='sm')
q-card-section
q-card-section
i18n-t(tag='span', keypath='admin.metrics.endpoint')
i18n-t(tag='span', keypath='admin.metrics.endpoint'
, scope='global'
)
template(#endpoint)
template(#endpoint)
strong.font-robotomono /metrics
strong.font-robotomono /metrics
.text-caption
{{
t
(
'admin.metrics.endpointWarning'
)
}}
.text-caption
{{
t
(
'admin.metrics.endpointWarning'
)
}}
...
@@ -66,7 +66,7 @@ q-page.admin-api
...
@@ -66,7 +66,7 @@ q-page.admin-api
q-card-section.col-auto.q-pr-none
q-card-section.col-auto.q-pr-none
q-icon(name='las la-key', size='sm')
q-icon(name='las la-key', size='sm')
q-card-section
q-card-section
i18n-t(tag='span', keypath='admin.metrics.auth')
i18n-t(tag='span', keypath='admin.metrics.auth'
, scope='global'
)
template(#headerName)
template(#headerName)
strong.font-robotomono Authorization
strong.font-robotomono Authorization
template(#tokenType)
template(#tokenType)
...
...
ux/src/pages/AdminSystem.vue
View file @
291fe262
...
@@ -32,7 +32,6 @@ q-page.admin-system
...
@@ -32,7 +32,6 @@ q-page.admin-system
icon='mdi-clipboard-text-outline'
icon='mdi-clipboard-text-outline'
label='Copy System Info'
label='Copy System Info'
color='primary'
color='primary'
@click=''
:disabled='state.loading > 0'
:disabled='state.loading > 0'
)
)
q-separator(inset)
q-separator(inset)
...
...
ux/src/pages/AdminTheme.vue
View file @
291fe262
...
@@ -65,7 +65,7 @@ q-page.admin-theme
...
@@ -65,7 +65,7 @@ q-page.admin-theme
unchecked-icon='las la-times'
unchecked-icon='las la-times'
:aria-label='t(`admin.theme.darkMode`)'
:aria-label='t(`admin.theme.darkMode`)'
)
)
template(v-for='
(cl, idx)
of colorKeys', :key='cl')
template(v-for='
cl
of colorKeys', :key='cl')
q-separator.q-my-sm(inset)
q-separator.q-my-sm(inset)
q-item
q-item
blueprint-icon(icon='fill-color')
blueprint-icon(icon='fill-color')
...
...
ux/src/pages/AdminUsers.vue
View file @
291fe262
...
@@ -39,7 +39,6 @@ q-page.admin-groups
...
@@ -39,7 +39,6 @@ q-page.admin-groups
unelevated
unelevated
color='secondary'
color='secondary'
:aria-label='t(`admin.users.defaults`)'
:aria-label='t(`admin.users.defaults`)'
@click=''
)
)
q-tooltip
{{
t
(
`admin.users.defaults`
)
}}
q-tooltip
{{
t
(
`admin.users.defaults`
)
}}
user-defaults-menu
user-defaults-menu
...
...
ux/src/pages/AdminUtilities.vue
View file @
291fe262
...
@@ -44,7 +44,6 @@ q-page.admin-utilities
...
@@ -44,7 +44,6 @@ q-page.admin-utilities
flat
flat
icon='las la-arrow-circle-right'
icon='las la-arrow-circle-right'
color='primary'
color='primary'
@click=''
:label='t(`common.actions.proceed`)'
:label='t(`common.actions.proceed`)'
)
)
q-item
q-item
...
@@ -57,7 +56,6 @@ q-page.admin-utilities
...
@@ -57,7 +56,6 @@ q-page.admin-utilities
flat
flat
icon='las la-arrow-circle-right'
icon='las la-arrow-circle-right'
color='primary'
color='primary'
@click=''
:label='t(`common.actions.proceed`)'
:label='t(`common.actions.proceed`)'
)
)
q-item
q-item
...
@@ -70,7 +68,6 @@ q-page.admin-utilities
...
@@ -70,7 +68,6 @@ q-page.admin-utilities
flat
flat
icon='las la-arrow-circle-right'
icon='las la-arrow-circle-right'
color='primary'
color='primary'
@click=''
:label='t(`common.actions.proceed`)'
:label='t(`common.actions.proceed`)'
)
)
q-item
q-item
...
@@ -83,7 +80,6 @@ q-page.admin-utilities
...
@@ -83,7 +80,6 @@ q-page.admin-utilities
flat
flat
icon='las la-arrow-circle-right'
icon='las la-arrow-circle-right'
color='primary'
color='primary'
@click=''
:label='t(`common.actions.proceed`)'
:label='t(`common.actions.proceed`)'
)
)
q-item
q-item
...
@@ -108,7 +104,6 @@ q-page.admin-utilities
...
@@ -108,7 +104,6 @@ q-page.admin-utilities
flat
flat
icon='las la-arrow-circle-right'
icon='las la-arrow-circle-right'
color='primary'
color='primary'
@click=''
:label='t(`common.actions.proceed`)'
:label='t(`common.actions.proceed`)'
)
)
q-item
q-item
...
@@ -121,7 +116,6 @@ q-page.admin-utilities
...
@@ -121,7 +116,6 @@ q-page.admin-utilities
flat
flat
icon='las la-arrow-circle-right'
icon='las la-arrow-circle-right'
color='primary'
color='primary'
@click=''
:label='t(`common.actions.proceed`)'
:label='t(`common.actions.proceed`)'
)
)
</
template
>
</
template
>
...
...
ux/src/pages/Index.vue
View file @
291fe262
...
@@ -390,7 +390,7 @@ function refreshTocExpanded (baseToc, lvl) {
...
@@ -390,7 +390,7 @@ function refreshTocExpanded (baseToc, lvl) {
}
}
}
}
.page-header
{
.page-header
{
min-
height
:
95px
;
height
:
95px
;
@at-root
.body--light
&
{
@at-root
.body--light
&
{
background
:
linear-gradient
(
to
bottom
,
$grey-2
0%
,
$grey-1
100%
);
background
:
linear-gradient
(
to
bottom
,
$grey-2
0%
,
$grey-1
100%
);
...
...
ux/src/pages/Search.vue
View file @
291fe262
...
@@ -67,7 +67,8 @@ q-layout(view='hHh Lpr lff')
...
@@ -67,7 +67,8 @@ q-layout(view='hHh Lpr lff')
q-item-section(side)
q-item-section(side)
q-checkbox(:model-value='scope.selected', @update:model-value='scope.toggleOption(scope.opt)', size='sm')
q-checkbox(:model-value='scope.selected', @update:model-value='scope.toggleOption(scope.opt)', size='sm')
q-item-section
q-item-section
q-item-label(v-html='scope.opt')
q-item-label
span(v-html='scope.opt')
//- q-input.q-mt-sm(
//- q-input.q-mt-sm(
//- outlined
//- outlined
//- dense
//- dense
...
@@ -103,7 +104,8 @@ q-layout(view='hHh Lpr lff')
...
@@ -103,7 +104,8 @@ q-layout(view='hHh Lpr lff')
q-item-section(side)
q-item-section(side)
q-checkbox(:model-value='scope.selected', @update:model-value='scope.toggleOption(scope.opt)')
q-checkbox(:model-value='scope.selected', @update:model-value='scope.toggleOption(scope.opt)')
q-item-section
q-item-section
q-item-label(v-html='scope.opt.name')
q-item-label
span(v-html='scope.opt.name')
q-select.q-mt-sm(
q-select.q-mt-sm(
outlined
outlined
v-model='state.params.filterEditor'
v-model='state.params.filterEditor'
...
@@ -154,7 +156,8 @@ q-layout(view='hHh Lpr lff')
...
@@ -154,7 +156,8 @@ q-layout(view='hHh Lpr lff')
q-item-section
q-item-section
q-item-label
{{
item
.
title
}}
q-item-label
{{
item
.
title
}}
q-item-label(v-if='item.description', caption)
{{
item
.
description
}}
q-item-label(v-if='item.description', caption)
{{
item
.
description
}}
q-item-label.text-highlight(v-if='item.highlight', caption, v-html='item.highlight')
q-item-label.text-highlight(v-if='item.highlight', caption)
span(v-html='item.highlight')
q-item-section(side)
q-item-section(side)
.flex.layout-search-itemtags
.flex.layout-search-itemtags
q-chip(
q-chip(
...
...
ux/src/stores/site.js
View file @
291fe262
...
@@ -64,7 +64,7 @@ export const useSiteStore = defineStore('site', {
...
@@ -64,7 +64,7 @@ export const useSiteStore = defineStore('site', {
},
},
sideDialogShown
:
false
,
sideDialogShown
:
false
,
sideDialogComponent
:
''
,
sideDialogComponent
:
''
,
docsBase
:
'https://
next
.js.wiki/docs'
,
docsBase
:
'https://
beta
.js.wiki/docs'
,
nav
:
{
nav
:
{
currentId
:
null
,
currentId
:
null
,
items
:
[]
items
:
[]
...
...
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