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
35bc7458
Commit
35bc7458
authored
May 21, 2019
by
Nick
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: image upload / display
parent
10a37276
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
295 additions
and
45 deletions
+295
-45
editor-markdown.vue
client/components/editor/editor-markdown.vue
+9
-3
editor-modal-media.vue
client/components/editor/editor-modal-media.vue
+120
-24
help.vue
client/components/editor/markdown/help.vue
+22
-1
editor-media-mutation-folder-create.gql
client/graph/editor/editor-media-mutation-folder-create.gql
+12
-0
editor-media-query-folder-list.gql
client/graph/editor/editor-media-query-folder-list.gql
+9
-0
editor-media-query-list.gql
client/graph/editor/editor-media-query-list.gql
+2
-2
v-btn.scss
client/scss/components/v-btn.scss
+4
-0
app.scss
client/themes/default/scss/app.scss
+28
-1
upload.js
server/controllers/upload.js
+27
-4
asset.js
server/graph/resolvers/asset.js
+34
-3
asset.graphql
server/graph/schemas/asset.graphql
+13
-5
error.js
server/helpers/error.js
+4
-0
assetFolders.js
server/models/assetFolders.js
+8
-0
assets.js
server/models/assets.js
+3
-2
No files found.
client/components/editor/editor-markdown.vue
View file @
35bc7458
<
template
lang=
'pug'
>
.editor-markdown
v-toolbar.editor-markdown-toolbar(dense, color='primary', dark, flat, style='overflow-x: hidden;')
v-btn.animated.fadeInLeft(v-if='isModalShown', flat, @click='closeAllModal')
v-icon(left) close
template(v-if='isModalShown')
v-spacer
v-btn.animated.fadeInRight(flat, @click='closeAllModal')
v-icon(left) remove_circle_outline
span Back to Editor
template(v-else)
v-tooltip(bottom, color='primary')
...
...
@@ -469,8 +471,12 @@ export default {
this
.
$root
.
$on
(
'editorInsert'
,
opts
=>
{
switch
(
opts
.
kind
)
{
case
'IMAGE'
:
let
img
=
`![
${
opts
.
text
}
](
${
opts
.
path
}
)`
if
(
opts
.
align
&&
opts
.
align
!==
''
)
{
img
+=
`{.align-
${
opts
.
align
}
}`
}
this
.
insertAtCursor
({
content
:
`![
${
opts
.
text
}
](
${
opts
.
path
}
)`
content
:
img
})
break
case
'BINARY'
:
...
...
client/components/editor/editor-modal-media.vue
View file @
35bc7458
...
...
@@ -8,9 +8,8 @@
.d-flex
v-toolbar.radius-7(color='teal lighten-5', dense, flat, height='44')
.body-2.teal--text Assets
v-btn.ml-3.my-0.radius-7(outline, large, color='teal', disabled, :icon='$vuetify.breakpoint.xsOnly')
v-icon(:left='$vuetify.breakpoint.mdAndUp') keyboard_arrow_up
span.hidden-sm-and-down Parent Folder
v-btn.ml-3.my-0.radius-7(outline, large, color='teal', icon, @click='refresh')
v-icon cached
v-dialog(v-model='newFolderDialog', max-width='550')
v-btn.my-0.mr-0.radius-7(outline, large, color='teal', :icon='$vuetify.breakpoint.xsOnly', slot='activator')
v-icon(:left='$vuetify.breakpoint.mdAndUp') add
...
...
@@ -28,13 +27,20 @@
@keyup.enter='createFolder'
@keyup.esc='newFolderDialog = false'
ref='folderNameIpt'
hint='Lowercase. No spaces allowed.'
persistent-hint
)
.caption.grey--text.text--darken-1.pl-5 Must follow the asset folder #[a(href='https://docs-beta.requarks.io/guide/assets#naming-restrictions', target='_blank') naming rules].
v-card-chin
v-spacer
v-btn(flat, @click='newFolderDialog = false') Cancel
v-btn(color='primary', @click='createFolder', :disabled='!isFolderNameValid') Create
v-btn(color='primary', @click='createFolder', :disabled='!isFolderNameValid', :loading='newFolderLoading') Create
template(v-if='folders.length > 0 || currentFolderId > 0')
.pt-2
v-btn.is-icon.mx-1(color='grey darken-2', outline, :dark='currentFolderId > 0', @click='upFolder()', :disabled='currentFolderId === 0')
v-icon keyboard_arrow_up
v-btn.btn-normalcase.mx-1(v-for='folder of folders', :key='folder.id', depressed, color='grey darken-2', dark, @click='downFolder(folder)')
v-icon(left) folder
span
{{
folder
.
name
}}
v-divider.mt-2
v-data-table(
:items='assets'
:headers='headers'
...
...
@@ -63,7 +69,7 @@
v-menu(offset-x)
v-btn.ma-0(icon, slot='activator')
v-icon(color='grey darken-2') more_horiz
v-list.py-0
v-list.py-0
(style='border-top: 5px solid #444;')
v-list-tile(@click='')
v-list-tile-avatar
v-icon(color='teal') short_text
...
...
@@ -72,25 +78,46 @@
template(v-if='props.item.kind === `IMAGE`')
v-list-tile(@click='')
v-list-tile-avatar
v-icon(color='green') image_search
v-list-tile-content Preview
v-divider
v-list-tile(@click='')
v-list-tile-avatar
v-icon(color='indigo') crop_rotate
v-list-tile-content Edit
v-divider
v-list-tile(@click='')
v-list-tile-avatar
v-icon(color='blue') keyboard
v-list-tile-content Rename / Move
v-icon(color='purple') offline_bolt
v-list-tile-content Optimize
v-divider
v-list-tile(@click='')
v-list-tile-avatar
v-icon(color='orange') keyboard
v-list-tile-content Rename
v-divider
v-list-tile(@click='')
v-list-tile-avatar
v-icon(color='blue') forward
v-list-tile-content Move
v-divider
v-list-tile(@click='')
v-list-tile-avatar
v-icon(color='red') delete
v-list-tile-content Delete
template(slot='no-data')
v-alert.m
a-3(icon='warning', :value='true', outline) No assets to displa
y.
v-alert.m
t-3.radius-7(icon='folder_open', :value='true', outline, color='teal') This asset folder is empt
y.
.text-xs-center.py-2(v-if='this.pageTotal > 1')
v-pagination(v-model='pagination.page', :length='pageTotal')
.d-flex.mt-3
v-toolbar.radius-7(flat, color='grey lighten-4', dense, height='44')
.body-2 / #[em root]
template(v-if='folderTree.length > 0')
.body-2
span.mr-1 /
template(v-for='folder of folderTree')
span(:key='folder.id')
{{
folder
.
name
}}
span.mx-1 /
.body-2(v-else) / #[em root]
template(v-if='$vuetify.breakpoint.smAndUp')
v-spacer
.body-1.grey--text.text--darken-1
{{
assets
.
length
}}
files
...
...
@@ -166,6 +193,8 @@ import vueFilePond from 'vue-filepond'
import
'filepond/dist/filepond.min.css'
import
listAssetQuery
from
'gql/editor/editor-media-query-list.gql'
import
listFolderAssetQuery
from
'gql/editor/editor-media-query-folder-list.gql'
import
createAssetFolderMutation
from
'gql/editor/editor-media-mutation-folder-create.gql'
const
FilePond
=
vueFilePond
()
const
localeSegmentRegex
=
/^
[
A-Z
]{2}(
-
[
A-Z
]{2})?
$/i
...
...
@@ -183,21 +212,27 @@ export default {
},
data
()
{
return
{
folders
:
[],
folderTree
:
[],
files
:
[],
assets
:
[],
pagination
:
{},
remoteImageUrl
:
''
,
imageAlignments
:
[
{
text
:
'None'
,
value
:
''
},
{
text
:
'Left'
,
value
:
'left'
},
{
text
:
'Centered'
,
value
:
'center'
},
{
text
:
'Right'
,
value
:
'right'
},
{
text
:
'Absolute Top Right'
,
value
:
'abstopright'
}
],
imageAlignment
:
''
,
currentFolderId
:
0
,
currentFileId
:
null
,
previousFolderId
:
0
,
loading
:
false
,
newFolderDialog
:
false
,
newFolderName
:
''
newFolderName
:
''
,
newFolderLoading
:
false
}
},
computed
:
{
...
...
@@ -219,7 +254,7 @@ export default {
{
text
:
'Filename'
,
value
:
'filename'
},
this
.
$vuetify
.
breakpoint
.
lgAndUp
&&
{
text
:
'Type'
,
value
:
'ext'
,
width
:
50
},
this
.
$vuetify
.
breakpoint
.
mdAndUp
&&
{
text
:
'File Size'
,
value
:
'fileSize'
,
width
:
110
},
this
.
$vuetify
.
breakpoint
.
mdAndUp
&&
{
text
:
'Added'
,
value
:
'createdAt'
,
width
:
1
50
},
this
.
$vuetify
.
breakpoint
.
mdAndUp
&&
{
text
:
'Added'
,
value
:
'createdAt'
,
width
:
1
75
},
this
.
$vuetify
.
breakpoint
.
smAndUp
&&
{
text
:
'Actions'
,
value
:
''
,
width
:
40
,
sortable
:
false
,
align
:
'right'
}
])
},
...
...
@@ -242,19 +277,17 @@ export default {
throw
new
TypeError
(
'Expected a number'
)
}
var
exponent
var
unit
var
neg
=
num
<
0
var
units
=
[
'B'
,
'kB'
,
'MB'
,
'GB'
,
'TB'
,
'PB'
,
'EB'
,
'ZB'
,
'YB'
]
let
exponent
let
unit
let
neg
=
num
<
0
let
units
=
[
'B'
,
'kB'
,
'MB'
,
'GB'
,
'TB'
,
'PB'
,
'EB'
,
'ZB'
,
'YB'
]
if
(
neg
)
{
num
=
-
num
}
if
(
num
<
1
)
{
return
(
neg
?
'-'
:
''
)
+
num
+
' B'
}
exponent
=
Math
.
min
(
Math
.
floor
(
Math
.
log
(
num
)
/
Math
.
log
(
1000
)),
units
.
length
-
1
)
num
=
(
num
/
Math
.
pow
(
1000
,
exponent
)).
toFixed
(
2
)
*
1
unit
=
units
[
exponent
]
...
...
@@ -263,12 +296,22 @@ export default {
}
},
methods
:
{
async
refresh
()
{
await
this
.
$apollo
.
queries
.
assets
.
refetch
()
this
.
$store
.
commit
(
'showNotification'
,
{
message
:
'List of assets refreshed successfully.'
,
style
:
'success'
,
icon
:
'check'
})
},
insert
()
{
const
asset
=
_
.
find
(
this
.
assets
,
[
'id'
,
this
.
currentFileId
])
const
assetPath
=
this
.
folderTree
.
map
(
f
=>
f
.
slug
).
join
(
'/'
)
this
.
$root
.
$emit
(
'editorInsert'
,
{
kind
:
asset
.
kind
,
path
:
`/
${
asset
.
filename
}
`
,
text
:
asset
.
filename
path
:
this
.
currentFolderId
>
0
?
`/
${
assetPath
}
/
${
asset
.
filename
}
`
:
`/
${
asset
.
filename
}
`
,
text
:
asset
.
filename
,
align
:
this
.
imageAlignment
})
this
.
activeModal
=
''
},
...
...
@@ -286,7 +329,7 @@ export default {
}
for
(
let
file
of
files
)
{
file
.
setMetadata
({
path
:
'test'
folderId
:
this
.
currentFolderId
})
}
await
this
.
$refs
.
pond
.
processFiles
()
...
...
@@ -305,15 +348,68 @@ export default {
await
this
.
$apollo
.
queries
.
assets
.
refetch
()
},
downFolder
(
folder
)
{
this
.
folderTree
.
push
(
folder
)
this
.
currentFolderId
=
folder
.
id
this
.
currentFileId
=
null
},
upFolder
()
{
this
.
folderTree
.
pop
()
const
parentFolder
=
_
.
last
(
this
.
folderTree
)
this
.
currentFolderId
=
parentFolder
?
parentFolder
.
id
:
0
this
.
currentFileId
=
null
},
async
createFolder
()
{
this
.
$store
.
commit
(
`loadingStart`
,
'editor-media-createfolder'
)
this
.
newFolderLoading
=
true
try
{
const
resp
=
await
this
.
$apollo
.
mutate
({
mutation
:
createAssetFolderMutation
,
variables
:
{
parentFolderId
:
this
.
currentFolderId
,
slug
:
this
.
newFolderName
}
})
if
(
_
.
get
(
resp
,
'data.assets.createFolder.responseResult.succeeded'
,
false
))
{
await
this
.
$apollo
.
queries
.
folders
.
refetch
()
this
.
$store
.
commit
(
'showNotification'
,
{
message
:
'Asset folder created successfully.'
,
style
:
'success'
,
icon
:
'check'
})
this
.
newFolderDialog
=
false
this
.
newFolderName
=
''
}
else
{
this
.
$store
.
commit
(
'pushGraphError'
,
new
Error
(
_
.
get
(
resp
,
'data.assets.createFolder.responseResult.message'
)))
}
}
catch
(
err
)
{
this
.
$store
.
commit
(
'pushGraphError'
,
err
)
}
this
.
newFolderLoading
=
false
this
.
$store
.
commit
(
`loadingStop`
,
'editor-media-createfolder'
)
}
},
apollo
:
{
folders
:
{
query
:
listFolderAssetQuery
,
variables
()
{
return
{
parentFolderId
:
this
.
currentFolderId
}
},
fetchPolicy
:
'network-only'
,
update
:
(
data
)
=>
data
.
assets
.
folders
,
watchLoading
(
isLoading
)
{
this
.
$store
.
commit
(
`loading
${
isLoading
?
'Start'
:
'Stop'
}
`
,
'editor-media-folders-list-refresh'
)
}
},
assets
:
{
query
:
listAssetQuery
,
variables
:
{
variables
()
{
return
{
folderId
:
this
.
currentFolderId
,
kind
:
'ALL'
}
},
throttle
:
1000
,
fetchPolicy
:
'network-only'
,
...
...
client/components/editor/markdown/help.vue
View file @
35bc7458
...
...
@@ -97,7 +97,17 @@
li Unordered List Item 1
li Unordered List Item 2
li Unordered List Item 3
.body-2.mt-3 Images
v-layout(row)
v-flex(xs6)
v-card.editor-markdown-help-source(flat)
v-card-text
div ![Caption Text](/path/to/image.jpg)
v-icon chevron_right
v-flex(xs6)
v-card.editor-markdown-help-result(flat)
v-card-text
img(src='https://via.placeholder.com/150x50.png')
v-flex(xs12, lg6, xl4)
v-card.radius-7.animated.fadeInUp.wait-p1s(light)
v-card-text
...
...
@@ -105,6 +115,17 @@
v-toolbar.radius-7(color='teal lighten-5', dense, flat, height='44')
v-icon.mr-3(color='teal') info
.body-2.teal--text Markdown Reference (continued)
.body-2.mt-3 Links
v-layout(row)
v-flex(xs6)
v-card.editor-markdown-help-source(flat)
v-card-text
div [Link Text](https://wiki.js.org)
v-icon chevron_right
v-flex(xs6)
v-card.editor-markdown-help-result(flat)
v-card-text
.caption: a(href='https://wiki.js.org', target='_blank') Link Text
.body-2.mt-3 Superscript
v-layout(row)
v-flex(xs6)
...
...
client/graph/editor/editor-media-mutation-folder-create.gql
0 → 100644
View file @
35bc7458
mutation
(
$parentFolderId
:
Int
!,
$slug
:
String
!)
{
assets
{
createFolder
(
parentFolderId
:
$parentFolderId
,
slug
:
$slug
)
{
responseResult
{
succeeded
errorCode
slug
message
}
}
}
}
client/graph/editor/editor-media-query-folder-list.gql
0 → 100644
View file @
35bc7458
query
(
$parentFolderId
:
Int
!)
{
assets
{
folders
(
parentFolderId
:
$parentFolderId
)
{
id
name
slug
}
}
}
client/graph/editor/editor-media-query-list.gql
View file @
35bc7458
query
(
$
root
:
String
,
$kind
:
AssetKind
!)
{
query
(
$
folderId
:
Int
!
,
$kind
:
AssetKind
!)
{
assets
{
list
(
root
:
$root
,
kind
:
$kind
)
{
list
(
folderId
:
$folderId
,
kind
:
$kind
)
{
id
filename
ext
...
...
client/scss/components/v-btn.scss
View file @
35bc7458
...
...
@@ -52,3 +52,7 @@
transform
:
scale
(
.7
)
rotateX
(
-180deg
);
}
}
.btn-normalcase
{
text-transform
:
none
;
}
client/themes/default/scss/app.scss
View file @
35bc7458
...
...
@@ -3,6 +3,7 @@
.contents
{
color
:
mc
(
'grey'
,
'800'
);
padding-bottom
:
50px
;
position
:
relative
;
@at-root
.theme--dark
&
{
color
:
mc
(
'grey'
,
'300'
);
...
...
@@ -309,7 +310,8 @@
}
&
:
:
before
{
color
:
mc
(
'grey'
,
'400'
);
content
:
''
;
display
:
none
;
}
@at-root
.theme--dark
&
{
...
...
@@ -457,4 +459,29 @@
}
}
// ---------------------------------
// IMAGES
// ---------------------------------
img
{
&
.align-left
{
float
:
left
;
margin
:
0
1rem
1rem
0
;
}
&
.align-right
{
float
:
right
;
margin
:
0
0
1rem
1rem
;
}
&
.align-center
{
display
:
block
;
max-width
:
100%
;
margin
:
auto
;
}
&
.align-abstopright
{
position
:
absolute
;
top
:
-90px
;
right
:
1rem
;
}
}
}
server/controllers/upload.js
View file @
35bc7458
...
...
@@ -41,11 +41,17 @@ router.post('/u', multer({
})
}
let
folderPath
=
''
// Get folder Id
let
folderId
=
null
try
{
const
folderRaw
=
_
.
get
(
req
,
'body.mediaUpload'
,
false
)
if
(
folderRaw
)
{
folderPath
=
_
.
get
(
JSON
.
parse
(
folderRaw
),
'path'
,
false
)
folderId
=
_
.
get
(
JSON
.
parse
(
folderRaw
),
'folderId'
,
null
)
if
(
folderId
===
0
)
{
folderId
=
null
}
}
else
{
throw
new
Error
(
'Missing File Metadata'
)
}
}
catch
(
err
)
{
return
res
.
status
(
400
).
json
({
...
...
@@ -54,17 +60,34 @@ router.post('/u', multer({
})
}
if
(
!
WIKI
.
auth
.
checkAccess
(
req
.
user
,
[
'write:assets'
],
{
path
:
`
${
folderPath
}
/
${
fileMeta
.
originalname
}
`
}))
{
// Build folder hierarchy
let
hierarchy
=
[]
if
(
folderId
)
{
try
{
hierarchy
=
await
WIKI
.
models
.
assetFolders
.
getHierarchy
(
folderId
)
}
catch
(
err
)
{
return
res
.
status
(
400
).
json
({
succeeded
:
false
,
message
:
'Failed to fetch folder hierarchy.'
})
}
}
// Check if user can upload at path
const
assetPath
=
(
folderId
)
?
opts
.
hierarchy
.
map
(
h
=>
h
.
slug
).
join
(
'/'
)
+
`/
${
fileMeta
.
originalname
}
`
:
fileMeta
.
originalname
if
(
!
WIKI
.
auth
.
checkAccess
(
req
.
user
,
[
'write:assets'
],
{
path
:
assetPath
}))
{
return
res
.
status
(
403
).
json
({
succeeded
:
false
,
message
:
'You are not authorized to upload files to this folder.'
})
}
// Process upload file
await
WIKI
.
models
.
assets
.
upload
({
...
fileMeta
,
originalname
:
sanitize
(
fileMeta
.
originalname
).
toLowerCase
(),
folder
:
folderPath
,
folderId
:
folderId
,
hierarchy
,
userId
:
req
.
user
.
id
})
res
.
send
(
'ok'
)
...
...
server/graph/resolvers/asset.js
View file @
35bc7458
const
sanitize
=
require
(
'sanitize-filename'
)
const
graphHelper
=
require
(
'../../helpers/graph'
)
/* global WIKI */
const
gql
=
require
(
'graphql'
)
module
.
exports
=
{
Query
:
{
async
assets
()
{
return
{}
}
...
...
@@ -13,7 +13,7 @@ module.exports = {
AssetQuery
:
{
async
list
(
obj
,
args
,
context
)
{
let
cond
=
{
folderId
:
null
folderId
:
args
.
folderId
===
0
?
null
:
args
.
folderId
}
if
(
args
.
kind
!==
'ALL'
)
{
cond
.
kind
=
args
.
kind
.
toLowerCase
()
...
...
@@ -23,9 +23,40 @@ module.exports = {
...
a
,
kind
:
a
.
kind
.
toUpperCase
()
}))
},
async
folders
(
obj
,
args
,
context
)
{
const
result
=
await
WIKI
.
models
.
assetFolders
.
query
().
where
({
parentId
:
args
.
parentFolderId
===
0
?
null
:
args
.
parentFolderId
})
// TODO: Filter by page rules
return
result
}
},
AssetMutation
:
{
async
createFolder
(
obj
,
args
,
context
)
{
try
{
const
folderSlug
=
sanitize
(
args
.
slug
).
toLowerCase
()
const
parentFolderId
=
args
.
parentFolderId
===
0
?
null
:
args
.
parentFolderId
const
result
=
await
WIKI
.
models
.
assetFolders
.
query
().
where
({
parentId
:
parentFolderId
,
slug
:
folderSlug
}).
first
()
if
(
!
result
)
{
await
WIKI
.
models
.
assetFolders
.
query
().
insert
({
slug
:
folderSlug
,
name
:
folderSlug
,
parentId
:
parentFolderId
})
return
{
responseResult
:
graphHelper
.
generateSuccess
(
'Asset Folder has been created successfully.'
)
}
}
else
{
throw
new
WIKI
.
Error
.
AssetFolderExists
()
}
}
catch
(
err
)
{
return
graphHelper
.
generateError
(
err
)
}
}
// deleteFile(obj, args) {
// return WIKI.models.File.destroy({
// where: {
...
...
server/graph/schemas/asset.graphql
View file @
35bc7458
...
...
@@ -16,9 +16,13 @@ extend type Mutation {
type
AssetQuery
{
list
(
root
:
String
kind
:
AssetKind
folderId
:
Int
!
kind
:
AssetKind
!
):
[
AssetItem
]
@
auth
(
requires
:
[
"
manage
:
system
"
,
"
read
:
assets
"
])
folders
(
parentFolderId
:
Int
!
):
[
AssetFolder
]
@
auth
(
requires
:
[
"
manage
:
system
"
,
"
read
:
assets
"
])
}
# -----------------------------------------------
...
...
@@ -26,9 +30,11 @@ type AssetQuery {
# -----------------------------------------------
type
AssetMutation
{
upload
(
data
:
Upload
!
):
DefaultResponse
createFolder
(
parentFolderId
:
Int
!
slug
:
String
!
name
:
String
):
DefaultResponse
@
auth
(
requires
:
[
"
manage
:
system
"
,
"
write
:
assets
"
])
}
# -----------------------------------------------
...
...
@@ -51,6 +57,8 @@ type AssetItem {
type
AssetFolder
{
id
:
Int
!
slug
:
String
!
name
:
String
}
enum
AssetKind
{
...
...
server/helpers/error.js
View file @
35bc7458
const
CustomError
=
require
(
'custom-error-instance'
)
module
.
exports
=
{
AssetFolderExists
:
CustomError
(
'AssetFolderExists'
,
{
message
:
'An asset folder with the same name already exists.'
,
code
:
2001
}),
AuthAccountBanned
:
CustomError
(
'AuthAccountBanned'
,
{
message
:
'Your account has been disabled.'
,
code
:
1016
...
...
server/models/assetFolders.js
View file @
35bc7458
...
...
@@ -32,4 +32,12 @@ module.exports = class AssetFolder extends Model {
}
}
}
static
async
getHierarchy
(
folderId
)
{
return
WIKI
.
models
.
knex
.
withRecursive
(
'ancestors'
,
qb
=>
{
qb
.
select
(
'id'
,
'name'
,
'slug'
,
'parentId'
).
from
(
'assetFolders'
).
where
(
'id'
,
folderId
).
union
(
sqb
=>
{
sqb
.
select
(
'a.id'
,
'a.name'
,
'a.slug'
,
'a.parentId'
).
from
(
'assetFolders AS a'
).
join
(
'ancestors'
,
'ancestors.parentId'
,
'a.id'
)
})
}).
select
(
'*'
).
from
(
'ancestors'
)
}
}
server/models/assets.js
View file @
35bc7458
...
...
@@ -67,7 +67,8 @@ module.exports = class Asset extends Model {
static
async
upload
(
opts
)
{
const
fileInfo
=
path
.
parse
(
opts
.
originalname
)
const
fileHash
=
assetHelper
.
generateHash
(
`
${
opts
.
folder
}
/
${
opts
.
originalname
}
`
)
const
folderPath
=
opts
.
hierarchy
.
map
(
h
=>
h
.
slug
).
join
(
'/'
)
const
fileHash
=
opts
.
folderId
?
assetHelper
.
generateHash
(
`
${
folderPath
}
/
${
opts
.
originalname
}
`
)
:
assetHelper
.
generateHash
(
opts
.
originalname
)
// Create asset entry
const
asset
=
await
WIKI
.
models
.
assets
.
query
().
insert
({
...
...
@@ -77,7 +78,7 @@ module.exports = class Asset extends Model {
kind
:
_
.
startsWith
(
opts
.
mimetype
,
'image/'
)
?
'image'
:
'binary'
,
mime
:
opts
.
mimetype
,
fileSize
:
opts
.
size
,
folderId
:
null
,
folderId
:
opts
.
folderId
,
authorId
:
opts
.
userId
})
...
...
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