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
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
176 additions
and
22 deletions
+176
-22
editor-markdown.vue
client/components/editor/editor-markdown.vue
+10
-4
editor-modal-media.vue
client/components/editor/editor-modal-media.vue
+0
-0
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
span Back to Editor
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')
v-btn.animated.fadeIn(icon, slot='activator', @click='toggleMarkup({ start: `**` })').mx-0
...
...
@@ -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
This diff is collapsed.
Click to expand it.
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