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
b6fd070b
Commit
b6fd070b
authored
Sep 08, 2019
by
Nick
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: list pages by tags + fix search permissions
parent
5202eade
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
184 additions
and
36 deletions
+184
-36
admin-pages.vue
client/components/admin/admin-pages.vue
+1
-1
tags.vue
client/components/tags.vue
+101
-8
common-pages-query-list.gql
client/graph/common/common-pages-query-list.gql
+14
-0
icon-info.svg
client/static/svg/icon-info.svg
+2
-0
page.js
server/graph/resolvers/page.js
+62
-26
page.graphql
server/graph/schemas/page.graphql
+4
-1
No files found.
client/components/admin/admin-pages.vue
View file @
b6fd070b
...
...
@@ -65,7 +65,7 @@
td
{{
props
.
item
.
updatedAt
|
moment
(
'calendar'
)
}}
template(slot='no-data')
v-alert.ma-3(icon='mdi-alert', :value='true', outlined) No pages to display.
.text-
xs-
center.py-2.animated.fadeInDown(v-if='this.pageTotal > 1')
.text-center.py-2.animated.fadeInDown(v-if='this.pageTotal > 1')
v-pagination(v-model='pagination', :length='pageTotal')
</
template
>
...
...
client/components/tags.vue
View file @
b6fd070b
...
...
@@ -15,7 +15,7 @@
v-icon(v-if='isSelected(tag.tag)', color='primary') mdi-checkbox-intermediate
v-icon(v-else) mdi-checkbox-blank-outline
v-list-item-title
{{
tag
.
title
}}
v-content
v-content
.grey(:class='$vuetify.theme.dark ? `darken-4-d5` : `lighten-3`')
v-toolbar(color='primary', dark, flat, height='58')
template(v-if='selection.length > 0')
.overline.mr-3.animated.fadeInLeft Current Selection
...
...
@@ -41,6 +41,7 @@
.overline.animated.fadeInRight Select one or more tags
v-toolbar(:color='$vuetify.theme.dark ? `grey darken-4-l5` : `grey lighten-4`', flat, height='58')
v-text-field.tags-search(
v-model='innerSearch'
label='Search within results...'
solo
hide-details
...
...
@@ -50,6 +51,7 @@
height='40'
prepend-icon='mdi-file-document-box-search-outline'
append-icon='mdi-arrow-right'
clearable
)
template(v-if='locales.length > 1')
v-divider.mx-3(vertical)
...
...
@@ -86,9 +88,62 @@
v-btn(text, height='40'): v-icon(size='20') mdi-chevron-double-up
v-btn(text, height='40'): v-icon(size='20') mdi-chevron-double-down
v-divider
.text-center.pt-10
.text-center.pt-10
(v-if='selection.length < 1')
img(src='/svg/icon-price-tag.svg')
.subtitle-2.grey--text Select one or more tags on the left.
.px-5.py-2(v-else)
v-data-iterator(
:items='pages'
:items-per-page='4'
:search='innerSearch'
:loading='isLoading'
:options.sync='pagination'
hide-default-footer
ref='dude'
)
template(v-slot:loading)
.text-center.pt-10
v-progress-circular(
indeterminate
color='primary'
size='96'
width='2'
)
.subtitle-2.grey--text.mt-5 Retrieving page results...
template(v-slot:no-data)
.text-center.pt-10
img(src='/svg/icon-info.svg')
.subtitle-2.grey--text Couldn't find any page with the selected tags.
template(v-slot:no-results)
.text-center.pt-10
img(src='/svg/icon-info.svg')
.subtitle-2.grey--text Couldn't find any page matching the current filtering options.
template(v-slot:default='props')
v-row(align='stretch')
v-col(
v-for='item of props.items'
:key='`page-` + item.id'
cols='12'
lg='6'
)
v-card.radius-7(
@click='goTo(item)'
style='height:100%;'
:class='$vuetify.theme.dark ? `grey darken-4` : ``'
)
v-card-text
.d-flex.flex-row.align-center
.body-1: strong.primary--text
{{
item
.
title
}}
v-spacer
.caption Last updated
{{
item
.
updatedAt
|
moment
(
'from'
)
}}
.body-2.grey--text
{{
item
.
description
||
'---'
}}
v-divider.my-2
.d-flex.flex-row.align-center
v-chip(small, label, :color='$vuetify.theme.dark ? `grey darken-3-l5` : `grey lighten-4`').overline
{{
item
.
locale
}}
.caption.ml-1 /
{{
item
.
path
}}
.text-center.py-2.animated.fadeInDown(v-if='this.pageTotal > 1')
v-pagination(v-model='pagination.page', :length='pageTotal')
nav-footer
notify
search-results
...
...
@@ -100,6 +155,7 @@ import VueRouter from 'vue-router'
import
_
from
'lodash'
import
tagsQuery
from
'gql/common/common-pages-query-tags.gql'
import
pagesQuery
from
'gql/common/common-pages-query-list.gql'
/* global siteLangs */
...
...
@@ -113,17 +169,27 @@ export default {
return
{
tags
:
[],
selection
:
[],
innerSearch
:
''
,
locale
:
'any'
,
locales
:
[],
orderBy
:
'
TITLE
'
,
orderBy
:
'
title
'
,
orderByItems
:
[
{
text
:
'Creation Date'
,
value
:
'
CREATED
'
},
{
text
:
'ID'
,
value
:
'
ID
'
},
{
text
:
'Last Modified'
,
value
:
'
UPDATED
'
},
{
text
:
'Path'
,
value
:
'
PATH
'
},
{
text
:
'Title'
,
value
:
'
TITLE
'
}
{
text
:
'Creation Date'
,
value
:
'
createdAt
'
},
{
text
:
'ID'
,
value
:
'
id
'
},
{
text
:
'Last Modified'
,
value
:
'
updatedAt
'
},
{
text
:
'Path'
,
value
:
'
path
'
},
{
text
:
'Title'
,
value
:
'
title
'
}
],
orderByDirection
:
0
,
pagination
:
{
page
:
1
,
itemsPerPage
:
12
,
mustSort
:
true
,
sortBy
:
[
'title'
],
sortDesc
:
[
false
]
},
pages
:
[],
isLoading
:
true
,
scrollStyle
:
{
vuescroll
:
{},
scrollPanel
:
{
...
...
@@ -154,6 +220,9 @@ export default {
},
tagsSelected
()
{
return
_
.
filter
(
this
.
tags
,
t
=>
_
.
includes
(
this
.
selection
,
t
.
tag
))
},
pageTotal
()
{
return
Math
.
ceil
(
this
.
pages
.
length
/
this
.
pagination
.
itemsPerPage
)
}
},
watch
:
{
...
...
@@ -162,9 +231,11 @@ export default {
},
orderBy
(
newValue
,
oldValue
)
{
this
.
rebuildURL
()
this
.
pagination
.
sortBy
=
[
newValue
]
},
orderByDirection
(
newValue
,
oldValue
)
{
this
.
rebuildURL
()
this
.
pagination
.
sortDesc
=
[
newValue
===
1
]
}
},
router
,
...
...
@@ -186,6 +257,7 @@ export default {
this
.
selection
.
push
(
tag
)
}
this
.
rebuildURL
()
console
.
info
(
this
.
$refs
.
dude
)
},
isSelected
(
tag
)
{
return
_
.
includes
(
this
.
selection
,
tag
)
...
...
@@ -204,6 +276,9 @@ export default {
_
.
set
(
urlObj
,
'query.dir'
,
this
.
orderByDirection
===
0
?
`asc`
:
`desc`
)
}
this
.
$router
.
push
(
urlObj
)
},
goTo
(
page
)
{
window
.
location
.
assign
(
`/
${
page
.
locale
}
/
${
page
.
path
}
`
)
}
},
apollo
:
{
...
...
@@ -214,6 +289,24 @@ export default {
watchLoading
(
isLoading
)
{
this
.
$store
.
commit
(
`loading
${
isLoading
?
'Start'
:
'Stop'
}
`
,
'tags-refresh'
)
}
},
pages
:
{
query
:
pagesQuery
,
fetchPolicy
:
'cache-and-network'
,
update
:
(
data
)
=>
_
.
cloneDeep
(
data
.
pages
.
list
),
watchLoading
(
isLoading
)
{
this
.
isLoading
=
isLoading
this
.
$store
.
commit
(
`loading
${
isLoading
?
'Start'
:
'Stop'
}
`
,
'pages-refresh'
)
},
variables
()
{
return
{
locale
:
this
.
locale
===
'any'
?
null
:
this
.
locale
,
tags
:
this
.
selection
}
},
skip
()
{
return
this
.
selection
.
length
<
1
}
}
}
}
...
...
client/graph/common/common-pages-query-list.gql
0 → 100644
View file @
b6fd070b
query
(
$limit
:
Int
,
$orderBy
:
PageOrderBy
,
$orderByDirection
:
PageOrderByDirection
,
$tags
:
[
String
!],
$locale
:
String
)
{
pages
{
list
(
limit
:
$limit
,
orderBy
:
$orderBy
,
orderByDirection
:
$orderByDirection
,
tags
:
$tags
,
locale
:
$locale
)
{
id
locale
path
title
description
createdAt
updatedAt
tags
}
}
}
client/static/svg/icon-info.svg
0 → 100644
View file @
b6fd070b
<svg
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 128 128"
width=
"256"
height=
"256"
><path
fill=
"#fff"
d=
"M64,14c27.6,0,50,22.4,50,50c0,27.6-22.4,50-50,50c-27.6,0-50-22.4-50-50C14,36.4,36.4,14,64,14"
/><path
fill=
"#e6e7e7"
d=
"M64,14c-0.2,0-0.4,0-0.6,0c-1.5,0-3.1,0.1-4.6,0.3c-0.3,0-0.7,0.1-1,0.1 c24.6,3.1,43.7,24.1,43.7,49.6c0,25.5-19.1,46.5-43.7,49.6c0.5,0.1,1,0.1,1.6,0.2c1.2,0.1,2.5,0.2,3.7,0.2c0.3,0,0.6,0,0.9,0 c27.6,0,50-22.4,50-50C114,36.4,91.6,14,64,14"
/><path
fill=
"#454b54"
d=
"M64,117c-29.2,0-53-23.8-53-53s23.8-53,53-53s53,23.8,53,53S93.2,117,64,117z M64,17 c-25.9,0-47,21.1-47,47s21.1,47,47,47s47-21.1,47-47S89.9,17,64,17z"
/><path
fill=
"#454b54"
d=
"M64 42.7c-1.7 0-3 1.3-3 3s1.3 3 3 3c1.7 0 3-1.3 3-3S65.7 42.7 64 42.7zM64 93c-1.7 0-3-1.3-3-3V62.3c0-1.7 1.3-3 3-3 1.7 0 3 1.3 3 3V90C67 91.7 65.7 93 64 93z"
/></svg>
\ No newline at end of file
server/graph/resolvers/page.js
View file @
b6fd070b
const
_
=
require
(
'lodash'
)
const
graphHelper
=
require
(
'../../helpers/graph'
)
/* global WIKI */
...
...
@@ -19,7 +20,16 @@ module.exports = {
},
async
search
(
obj
,
args
,
context
)
{
if
(
WIKI
.
data
.
searchEngine
)
{
return
WIKI
.
data
.
searchEngine
.
query
(
args
.
query
,
args
)
const
resp
=
await
WIKI
.
data
.
searchEngine
.
query
(
args
.
query
,
args
)
return
{
...
resp
,
results
:
_
.
filter
(
resp
.
results
,
r
=>
{
return
WIKI
.
auth
.
checkAccess
(
context
.
req
.
user
,
[
'read:pages'
],
{
path
:
r
.
path
,
locale
:
r
.
locale
})
})
}
}
else
{
return
{
results
:
[],
...
...
@@ -29,8 +39,8 @@ module.exports = {
}
},
async
list
(
obj
,
args
,
context
,
info
)
{
return
WIKI
.
models
.
pages
.
query
().
column
([
'id'
,
let
results
=
await
WIKI
.
models
.
pages
.
query
().
column
([
'
pages.
id'
,
'path'
,
{
locale
:
'localeCode'
},
'title'
,
...
...
@@ -41,29 +51,55 @@ module.exports = {
'contentType'
,
'createdAt'
,
'updatedAt'
]).
modify
(
queryBuilder
=>
{
if
(
args
.
limit
)
{
queryBuilder
.
limit
(
args
.
limit
)
}
const
orderDir
=
args
.
orderByDirection
===
'DESC'
?
'desc'
:
'asc'
switch
(
args
.
orderBy
)
{
case
'CREATED'
:
queryBuilder
.
orderBy
(
'createdAt'
,
orderDir
)
break
case
'PATH'
:
queryBuilder
.
orderBy
(
'path'
,
orderDir
)
break
case
'TITLE'
:
queryBuilder
.
orderBy
(
'title'
,
orderDir
)
break
case
'UPDATED'
:
queryBuilder
.
orderBy
(
'updatedAt'
,
orderDir
)
break
default
:
queryBuilder
.
orderBy
(
'id'
,
orderDir
)
break
}
})
])
.
eagerAlgorithm
(
WIKI
.
models
.
Objection
.
Model
.
JoinEagerAlgorithm
)
.
eager
(
'tags(selectTags)'
,
{
selectTags
:
builder
=>
{
builder
.
select
(
'tag'
)
}
})
.
modify
(
queryBuilder
=>
{
if
(
args
.
limit
)
{
queryBuilder
.
limit
(
args
.
limit
)
}
if
(
args
.
locale
)
{
queryBuilder
.
where
(
'localeCode'
,
args
.
locale
)
}
if
(
args
.
tags
&&
args
.
tags
.
length
>
0
)
{
queryBuilder
.
whereIn
(
'tags.tag'
,
args
.
tags
)
}
const
orderDir
=
args
.
orderByDirection
===
'DESC'
?
'desc'
:
'asc'
switch
(
args
.
orderBy
)
{
case
'CREATED'
:
queryBuilder
.
orderBy
(
'createdAt'
,
orderDir
)
break
case
'PATH'
:
queryBuilder
.
orderBy
(
'path'
,
orderDir
)
break
case
'TITLE'
:
queryBuilder
.
orderBy
(
'title'
,
orderDir
)
break
case
'UPDATED'
:
queryBuilder
.
orderBy
(
'updatedAt'
,
orderDir
)
break
default
:
queryBuilder
.
orderBy
(
'pages.id'
,
orderDir
)
break
}
})
results
=
_
.
filter
(
results
,
r
=>
{
return
WIKI
.
auth
.
checkAccess
(
context
.
req
.
user
,
[
'read:pages'
],
{
path
:
r
.
path
,
locale
:
r
.
locale
})
}).
map
(
r
=>
({
...
r
,
tags
:
_
.
map
(
r
.
tags
,
'tag'
)
}))
if
(
args
.
tags
&&
args
.
tags
.
length
>
0
)
{
results
=
_
.
filter
(
results
,
r
=>
_
.
every
(
args
.
tags
,
t
=>
_
.
includes
(
r
.
tags
,
t
)))
}
return
results
},
async
single
(
obj
,
args
,
context
,
info
)
{
let
page
=
await
WIKI
.
models
.
pages
.
getPageFromDb
(
args
.
id
)
...
...
server/graph/schemas/page.graphql
View file @
b6fd070b
...
...
@@ -31,7 +31,9 @@ type PageQuery {
limit
:
Int
orderBy
:
PageOrderBy
orderByDirection
:
PageOrderByDirection
):
[
PageListItem
!]!
@
auth
(
requires
:
[
"
manage
:
system
"
])
tags
:
[
String
!]
locale
:
String
):
[
PageListItem
!]!
@
auth
(
requires
:
[
"
manage
:
system
"
,
"
read
:
pages
"
])
single
(
id
:
Int
!
...
...
@@ -177,6 +179,7 @@ type PageListItem {
privateNS
:
String
createdAt
:
Date
!
updatedAt
:
Date
!
tags
:
[
String
]
}
enum
PageOrderBy
{
...
...
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