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
13f17297
Commit
13f17297
authored
Jul 02, 2019
by
Nick
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: pages admin + path parsing fixes
parent
4f968cf2
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
454 additions
and
45 deletions
+454
-45
admin.vue
client/components/admin.vue
+4
-0
admin-navigation.vue
client/components/admin/admin-navigation.vue
+6
-2
admin-pages-edit.vue
client/components/admin/admin-pages-edit.vue
+236
-0
admin-pages.vue
client/components/admin/admin-pages.vue
+41
-9
pages-query-single.gql
client/graph/admin/pages/pages-query-single.gql
+27
-0
icon-view-details.svg
client/static/svg/icon-view-details.svg
+44
-0
common.js
server/controllers/common.js
+32
-16
page.js
server/graph/resolvers/page.js
+12
-0
page.graphql
server/graph/schemas/page.graphql
+27
-0
error.js
server/helpers/error.js
+4
-0
page.js
server/helpers/page.js
+3
-1
master.js
server/master.js
+1
-0
pages.js
server/models/pages.js
+17
-17
No files found.
client/components/admin.vue
View file @
13f17297
...
...
@@ -56,6 +56,9 @@
v-list-tile(to='/auth')
v-list-tile-avatar: v-icon lock_outline
v-list-tile-title
{{
$t
(
'admin:auth.title'
)
}}
v-list-tile(to='/comments', disabled)
v-list-tile-avatar: v-icon(color='grey lighten-2') comment
v-list-tile-title
{{
$t
(
'admin:comments.title'
)
}}
v-list-tile(to='/editor', disabled)
v-list-tile-avatar: v-icon(color='grey lighten-2') transform
v-list-tile-title
{{
$t
(
'admin:editor.title'
)
}}
...
...
@@ -141,6 +144,7 @@ const router = new VueRouter({
{
path
:
'/locale'
,
component
:
()
=>
import
(
/* webpackChunkName: "admin" */
'./admin/admin-locale.vue'
)
},
{
path
:
'/navigation'
,
component
:
()
=>
import
(
/* webpackChunkName: "admin" */
'./admin/admin-navigation.vue'
)
},
{
path
:
'/pages'
,
component
:
()
=>
import
(
/* webpackChunkName: "admin" */
'./admin/admin-pages.vue'
)
},
{
path
:
'/pages/:id'
,
component
:
()
=>
import
(
/* webpackChunkName: "admin" */
'./admin/admin-pages-edit.vue'
)
},
{
path
:
'/theme'
,
component
:
()
=>
import
(
/* webpackChunkName: "admin" */
'./admin/admin-theme.vue'
)
},
{
path
:
'/groups'
,
component
:
()
=>
import
(
/* webpackChunkName: "admin" */
'./admin/admin-groups.vue'
)
},
{
path
:
'/groups/:id'
,
component
:
()
=>
import
(
/* webpackChunkName: "admin" */
'./admin/admin-groups-edit.vue'
)
},
...
...
client/components/admin/admin-navigation.vue
View file @
13f17297
...
...
@@ -97,6 +97,7 @@
v
-
else
-
if
=
'current.targetType === "page"'
color
=
'indigo'
dark
@
click
=
'selectPage'
)
v
-
icon
(
left
)
search
span
Select
Page
...
...
...
@@ -162,8 +163,8 @@ export default {
return
[
{
text
:
this
.
$t
(
'navigation.navType.external'
),
value
:
'external'
}
,
{
text
:
this
.
$t
(
'navigation.navType.home'
),
value
:
'home'
}
,
{
text
:
this
.
$t
(
'navigation.navType.page'
),
value
:
'page'
}
,
{
text
:
this
.
$t
(
'navigation.navType.searchQuery'
),
value
:
'search'
}
{
text
:
this
.
$t
(
'navigation.navType.page'
),
value
:
'page'
}
//
{
text
:
this
.
$t
(
'navigation.navType.searchQuery'
),
value
:
'search'
}
]
}
}
,
...
...
@@ -197,6 +198,9 @@ export default {
selectItem
(
item
)
{
this
.
current
=
item
}
,
selectPage
()
{
window
.
alert
(
`Coming soon. Use External Link for now (you can still specify internal links).`
)
}
,
async
save
()
{
this
.
$store
.
commit
(
`loadingStart`
,
'admin-navigation-save'
)
try
{
...
...
client/components/admin/admin-pages-edit.vue
0 → 100644
View file @
13f17297
<
template
lang=
'pug'
>
v-container(fluid, grid-list-lg)
v-layout(row, wrap, v-if='page.id')
v-flex(xs12)
.admin-header
img.animated.fadeInUp(src='/svg/icon-view-details.svg', alt='Edit Page', style='width: 80px;')
.admin-header-title
.headline.blue--text.text--darken-2.animated.fadeInLeft Page Details
.subheading.grey--text.animated.fadeInLeft.wait-p2s
v-chip.ml-0.mr-2(label, small).caption ID
{{
page
.
id
}}
span /
{{
page
.
locale
}}
/
{{
page
.
path
}}
v-spacer
template(v-if='page.isPublished')
status-indicator.mr-3(positive, pulse)
.caption.green--text
{{
$t
(
'common:page.published'
)
}}
template(v-else)
status-indicator.mr-3(negative, pulse)
.caption.red--text
{{
$t
(
'common:page.unpublished'
)
}}
template(v-if='page.isPrivate')
status-indicator.mr-3.ml-4(intermediary, pulse)
.caption.deep-orange--text
{{
$t
(
'common:page.private'
)
}}
template(v-else)
status-indicator.mr-3.ml-4(active, pulse)
.caption.blue--text
{{
$t
(
'common:page.global'
)
}}
v-spacer
v-btn.animated.fadeInRight.wait-p4s(color='grey', large, outline, to='/pages')
v-icon arrow_back
v-divider.animated.fadeInRight.wait-p3s.mx-3(vertical)
v-dialog(v-model='deletePageDialog', max-width='500')
v-btn.animated.fadeInDown.wait-p1s(color='red', large, outline, slot='activator')
v-icon(color='red') delete
v-card.wiki-form
.dialog-header.is-short.is-red
v-icon.mr-2(color='white') highlight_off
span
{{
$t
(
'common:page.delete'
)
}}
v-card-text
i18next.body-2(path='common:page.deleteTitle', tag='div')
span.red--text.text--darken-2(place='title')
{{
page
.
title
}}
.caption
{{
$t
(
'common:page.deleteSubtitle'
)
}}
v-chip.mt-3.ml-0.mr-1(label, color='red lighten-4', disabled, small)
.caption.red--text.text--darken-2
{{
page
.
locale
.
toUpperCase
()
}}
v-chip.mt-3.mx-0(label, color='red lighten-5', disabled, small)
span.red--text.text--darken-2 /
{{
page
.
path
}}
v-card-chin
v-spacer
v-btn(flat, @click='deletePageDialog = false', :disabled='loading')
{{
$t
(
'common:actions.cancel'
)
}}
v-btn(color='red darken-2', @click='deletePage', :loading='loading').white--text
{{
$t
(
'common:actions.delete'
)
}}
v-btn.animated.fadeInDown(color='teal', large, outline, @click='rerenderPage')
v-icon(left) system_update_alt
span Re-render
v-flex(xs12, lg6)
v-card.animated.fadeInUp
v-toolbar(color='primary', dense, dark, flat)
v-icon.mr-2 subject
span Properties
v-list.py-0(two-line, dense)
v-list-tile
v-list-tile-content
v-list-tile-title.caption.grey--text Title
v-list-tile-sub-title.body-2.grey--text.text--darken-3
{{
page
.
title
}}
v-divider
v-list-tile
v-list-tile-content
v-list-tile-title.caption.grey--text Description
v-list-tile-sub-title.body-2.grey--text.text--darken-3
{{
page
.
description
||
'-'
}}
v-divider
v-list-tile
v-list-tile-content
v-list-tile-title.caption.grey--text Locale
v-list-tile-sub-title.body-2.grey--text.text--darken-3
{{
page
.
locale
}}
v-list-tile-action
v-btn(icon)
v-icon(color='grey') edit
v-divider
v-list-tile
v-list-tile-content
v-list-tile-title.caption.grey--text Path
v-list-tile-sub-title.body-2.grey--text.text--darken-3
{{
page
.
path
}}
v-list-tile-action
v-btn(icon)
v-icon(color='grey') edit
v-divider
v-list-tile
v-list-tile-content
v-list-tile-title.caption.grey--text Editor
v-list-tile-sub-title.body-2.grey--text.text--darken-3
{{
page
.
editor
||
'?'
}}
v-divider
v-list-tile
v-list-tile-content
v-list-tile-title.caption.grey--text Content Type
v-list-tile-sub-title.body-2.grey--text.text--darken-3
{{
page
.
contentType
||
'?'
}}
v-toolbar.elevation-2.mt-3.animated.fadeInUp.wait-p4s(color='white', dense)
v-spacer
v-btn(color='primary', flat, :href='`/` + page.locale + `/` + page.path')
v-icon(left) subject
span View
v-divider(vertical)
v-btn(color='primary', flat, :href='`/e/` + page.locale + `/` + page.path')
v-icon(left) edit
span Edit
v-divider(vertical)
v-btn(color='primary', flat, :href='`/s/` + page.locale + `/` + page.path')
v-icon(left) code
span Source
v-divider(vertical)
v-btn(color='primary', flat, :href='`/h/` + page.locale + `/` + page.path')
v-icon(left) history
span History
v-spacer
.caption.mt-4.grey--text.animated.fadeInUp.wait-p6s Page Hash:
{{
page
.
hash
}}
v-flex(xs12, lg6)
v-card.animated.fadeInUp.wait-p2s
v-toolbar(color='primary', dense, dark, flat)
v-icon.mr-2 people
span Users
v-list.py-0(two-line, dense)
v-list-tile
v-list-tile-avatar
v-btn(icon, :to='`/users/` + page.creatorId')
v-icon(color='grey') person
v-list-tile-content
v-list-tile-title.caption.grey--text Creator
v-list-tile-sub-title.body-2.grey--text.text--darken-3
{{
page
.
creatorName
}}
#[em.caption (
{{
page
.
creatorEmail
}}
)]
v-list-tile-action
v-list-tile-action-text
{{
page
.
createdAt
|
moment
(
'calendar'
)
}}
v-divider
v-list-tile
v-list-tile-avatar
v-btn(icon, :to='`/users/` + page.authorId')
v-icon(color='grey') person
v-list-tile-content
v-list-tile-title.caption.grey--text Last Editor
v-list-tile-sub-title.body-2.grey--text.text--darken-3
{{
page
.
authorName
}}
#[em.caption (
{{
page
.
authorEmail
}}
)]
v-list-tile-action
v-list-tile-action-text
{{
page
.
updatedAt
|
moment
(
'calendar'
)
}}
v-card.mt-3.animated.fadeInUp.wait-p4s
v-toolbar(color='primary', dense, dark, flat)
v-icon.mr-2 history
span Recent History
v-spacer
v-chip(label, color='white', small).primary--text coming soon
v-timeline.mx-3(dense, clipped)
v-timeline-item(color='teal', small, v-if='page.createdAt !== page.updatedAt')
v-layout(justify-space-between)
v-flex(xs7) Page Modified by #[strong
{{
page
.
authorName
}}
] #[em.caption (
{{
page
.
authorEmail
}}
)]
v-flex.text-xs-right(xs5).caption.grey--text.text-darken-2
{{
page
.
updatedAt
|
moment
(
'calendar'
)
}}
v-timeline-item(hide-dot, small)
.body-1 ...
v-btn.mx-0(outline, color='grey') View Full History
.body-1 ...
v-timeline-item(color='pink', small)
v-layout(justify-space-between)
v-flex(xs7) Page created by #[strong
{{
page
.
creatorName
}}
] #[em.caption (
{{
page
.
creatorEmail
}}
)]
v-flex.text-xs-right(xs5).caption.grey--text.text-darken-2
{{
page
.
createdAt
|
moment
(
'calendar'
)
}}
v-layout(row, align-center, v-else)
v-progress-circular(indeterminate, width='2', color='grey')
.body-2.pl-3.grey--text
{{
$t
(
'common:page.loading'
)
}}
</
template
>
<
script
>
import
_
from
'lodash'
import
{
StatusIndicator
}
from
'vue-status-indicator'
import
pageQuery
from
'gql/admin/pages/pages-query-single.gql'
import
deletePageMutation
from
'gql/common/common-pages-mutation-delete.gql'
export
default
{
components
:
{
StatusIndicator
},
data
()
{
return
{
deletePageDialog
:
false
,
page
:
{},
loading
:
false
}
},
methods
:
{
async
deletePage
()
{
this
.
loading
=
true
this
.
$store
.
commit
(
`loadingStart`
,
'page-delete'
)
try
{
const
resp
=
await
this
.
$apollo
.
mutate
({
mutation
:
deletePageMutation
,
variables
:
{
id
:
this
.
page
.
id
}
})
if
(
_
.
get
(
resp
,
'data.pages.delete.responseResult.succeeded'
,
false
))
{
this
.
$store
.
commit
(
'showNotification'
,
{
style
:
'green'
,
message
:
`Page deleted successfully.`
,
icon
:
'check'
})
this
.
$router
.
replace
(
'/pages'
)
}
else
{
throw
new
Error
(
_
.
get
(
resp
,
'data.pages.delete.responseResult.message'
,
this
.
$t
(
'common:error.unexpected'
)))
}
}
catch
(
err
)
{
this
.
$store
.
commit
(
'pushGraphError'
,
err
)
}
this
.
$store
.
commit
(
`loadingStop`
,
'page-delete'
)
},
async
rerenderPage
()
{
this
.
$store
.
commit
(
'showNotification'
,
{
style
:
'indigo'
,
message
:
`Coming soon...`
,
icon
:
'directions_boat'
})
}
},
apollo
:
{
page
:
{
query
:
pageQuery
,
variables
()
{
return
{
id
:
_
.
toSafeInteger
(
this
.
$route
.
params
.
id
)
}
},
fetchPolicy
:
'network-only'
,
update
:
(
data
)
=>
data
.
pages
.
single
,
watchLoading
(
isLoading
)
{
this
.
$store
.
commit
(
`loading
${
isLoading
?
'Start'
:
'Stop'
}
`
,
'admin-pages-refresh'
)
}
}
}
}
</
script
>
<
style
lang=
'scss'
>
</
style
>
client/components/admin/admin-pages.vue
View file @
13f17297
...
...
@@ -3,17 +3,17 @@
v-layout(row wrap)
v-flex(xs12)
.admin-header
img(src='/svg/icon-file.svg', alt='Page', style='width: 80px;')
img
.animated.fadeInUp
(src='/svg/icon-file.svg', alt='Page', style='width: 80px;')
.admin-header-title
.headline.blue--text.text--darken-2 Pages
.subheading.grey--text
Manage pages #[v-chip(label, color='primary', small).white--text coming soon]
.headline.blue--text.text--darken-2
.animated.fadeInLeft
Pages
.subheading.grey--text
.animated.fadeInLeft.wait-p2s Manage pages
v-spacer
v-btn(color='grey', outline, @click='refresh', large)
v-btn
.animated.fadeInDown.wait-p1s
(color='grey', outline, @click='refresh', large)
v-icon.grey--text refresh
v-btn(color='primary', depressed, large, @click='newpage', disabled)
v-btn
.animated.fadeInDown
(color='primary', depressed, large, @click='newpage', disabled)
v-icon(left) add
span New Page
v-card.wiki-form.mt-3
v-card.wiki-form.mt-3
.animated.fadeInUp
v-toolbar(flat, :color='$vuetify.dark ? `grey darken-3-d5` : `grey lighten-5`', height='80')
v-spacer
v-text-field(
...
...
@@ -29,17 +29,21 @@
hide-details
single-line
label='Locale'
:items='langs'
v-model='selectedLang'
)
v-select.ml-2(
outline
hide-details
single-line
label='Publish State'
:items='states'
v-model='selectedState'
)
v-spacer
v-divider
v-data-table(
:items='
p
ages'
:items='
filteredP
ages'
:headers='headers'
:search='search'
:pagination.sync='pagination'
...
...
@@ -61,11 +65,12 @@
td
{{
props
.
item
.
updatedAt
|
moment
(
'calendar'
)
}}
template(slot='no-data')
v-alert.ma-3(icon='warning', :value='true', outline) No pages to display.
.text-xs-center.py-2(v-if='this.pageTotal > 1')
.text-xs-center.py-2
.animated.fadeInDown
(v-if='this.pageTotal > 1')
v-pagination(v-model='pagination.page', :length='pageTotal')
</
template
>
<
script
>
import
_
from
'lodash'
import
pagesQuery
from
'gql/admin/pages/pages-query-list.gql'
export
default
{
...
...
@@ -82,6 +87,13 @@ export default {
{
text
:
'Last Updated'
,
value
:
'updatedAt'
,
width
:
250
}
],
search
:
''
,
selectedLang
:
null
,
selectedState
:
null
,
states
:
[
{
text
:
'All Publishing States'
,
value
:
null
},
{
text
:
'Published'
,
value
:
true
},
{
text
:
'Not Published'
,
value
:
false
}
],
loading
:
false
}
},
...
...
@@ -91,7 +103,27 @@ export default {
return
0
}
return
Math
.
ceil
(
this
.
pages
.
length
/
this
.
pagination
.
rowsPerPage
)
return
Math
.
ceil
(
this
.
filteredPages
.
length
/
this
.
pagination
.
rowsPerPage
)
},
filteredPages
()
{
return
_
.
filter
(
this
.
pages
,
pg
=>
{
if
(
this
.
selectedLang
!==
null
&&
this
.
selectedLang
!==
pg
.
locale
)
{
return
false
}
if
(
this
.
selectedState
!==
null
&&
this
.
selectedState
!==
pg
.
isPublished
)
{
return
false
}
return
true
})
},
langs
()
{
return
_
.
concat
({
text
:
'All Locales'
,
value
:
null
},
_
.
uniqBy
(
this
.
pages
,
'locale'
).
map
(
pg
=>
({
text
:
pg
.
locale
,
value
:
pg
.
locale
})))
}
},
methods
:
{
...
...
client/graph/admin/pages/pages-query-single.gql
0 → 100644
View file @
13f17297
query
(
$id
:
Int
!)
{
pages
{
single
(
id
:
$id
)
{
id
path
hash
title
description
isPrivate
isPublished
privateNS
publishStartDate
publishEndDate
contentType
createdAt
updatedAt
editor
locale
authorId
authorName
authorEmail
creatorId
creatorName
creatorEmail
}
}
}
client/static/svg/icon-view-details.svg
0 → 100644
View file @
13f17297
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
version=
"1.1"
id=
"Слой_1"
x=
"0px"
y=
"0px"
viewBox=
"0 0 64 64"
style=
"enable-background:new 0 0 64 64;"
xml:space=
"preserve"
width=
"96px"
height=
"96px"
>
<linearGradient
id=
"SVGID_1__44764"
gradientUnits=
"userSpaceOnUse"
x1=
"43"
y1=
"28.8333"
x2=
"43"
y2=
"37.5036"
spreadMethod=
"reflect"
>
<stop
offset=
"0"
style=
"stop-color:#6DC7FF"
/>
<stop
offset=
"1"
style=
"stop-color:#E6ABFF"
/>
</linearGradient>
<path
style=
"fill:url(#SVGID_1__44764);"
d=
"M47,33c0,2.21-1.79,4-4,4s-4-1.79-4-4c0-2.21,1.79-4,4-4C43,31.21,44.79,33,47,33z"
/>
<linearGradient
id=
"SVGID_2__44764"
gradientUnits=
"userSpaceOnUse"
x1=
"16"
y1=
"36.8333"
x2=
"16"
y2=
"43.3275"
spreadMethod=
"reflect"
>
<stop
offset=
"0"
style=
"stop-color:#6DC7FF"
/>
<stop
offset=
"1"
style=
"stop-color:#E6ABFF"
/>
</linearGradient>
<path
style=
"fill:url(#SVGID_2__44764);"
d=
"M20,42c0,0.552-0.448,1-1,1h-6c-0.552,0-1-0.448-1-1v-4c0-0.552,0.448-1,1-1h6 c0.552,0,1,0.448,1,1V42z"
/>
<linearGradient
id=
"SVGID_3__44764"
gradientUnits=
"userSpaceOnUse"
x1=
"32"
y1=
"7.375"
x2=
"32"
y2=
"57.825"
spreadMethod=
"reflect"
>
<stop
offset=
"0"
style=
"stop-color:#1A6DFF"
/>
<stop
offset=
"1"
style=
"stop-color:#C822FF"
/>
</linearGradient>
<path
style=
"fill:url(#SVGID_3__44764);"
d=
"M58,33c0-4.335-7.062-10.397-14-10.946V11c0-1.654-1.346-3-3-3H9c-1.654,0-3,1.346-3,3v38v4 c0,1.654,1.346,3,3,3h32c1.654,0,3-1.346,3-3v-4v-5.054C50.938,43.397,58,37.335,58,33z M41,54H9c-0.552,0-1-0.448-1-1v-1.184 C8.314,51.928,8.648,52,9,52h32c0.352,0,0.686-0.072,1-0.184V53C42,53.552,41.552,54,41,54z M41,50H9c-0.552,0-1-0.448-1-1V11 c0-0.552,0.448-1,1-1h32c0.552,0,1,0.448,1,1v11.054C35.062,22.603,28,28.665,28,33s7.062,10.397,14,10.946V49 C42,49.552,41.552,50,41,50z M43,42c-6.317,0-13-5.853-13-9s6.683-9,13-9s13,5.853,13,9S49.317,42,43,42z"
/>
<linearGradient
id=
"SVGID_4__44764"
gradientUnits=
"userSpaceOnUse"
x1=
"20"
y1=
"7.375"
x2=
"20"
y2=
"57.825"
spreadMethod=
"reflect"
>
<stop
offset=
"0"
style=
"stop-color:#1A6DFF"
/>
<stop
offset=
"1"
style=
"stop-color:#C822FF"
/>
</linearGradient>
<rect
x=
"12"
y=
"21"
style=
"fill:url(#SVGID_4__44764);"
width=
"16"
height=
"2"
/>
<linearGradient
id=
"SVGID_5__44764"
gradientUnits=
"userSpaceOnUse"
x1=
"19"
y1=
"7.375"
x2=
"19"
y2=
"57.825"
spreadMethod=
"reflect"
>
<stop
offset=
"0"
style=
"stop-color:#1A6DFF"
/>
<stop
offset=
"1"
style=
"stop-color:#C822FF"
/>
</linearGradient>
<rect
x=
"12"
y=
"25"
style=
"fill:url(#SVGID_5__44764);"
width=
"14"
height=
"2"
/>
<linearGradient
id=
"SVGID_6__44764"
gradientUnits=
"userSpaceOnUse"
x1=
"17"
y1=
"7.375"
x2=
"17"
y2=
"57.825"
spreadMethod=
"reflect"
>
<stop
offset=
"0"
style=
"stop-color:#1A6DFF"
/>
<stop
offset=
"1"
style=
"stop-color:#C822FF"
/>
</linearGradient>
<rect
x=
"12"
y=
"29"
style=
"fill:url(#SVGID_6__44764);"
width=
"10"
height=
"2"
/>
<linearGradient
id=
"SVGID_7__44764"
gradientUnits=
"userSpaceOnUse"
x1=
"16"
y1=
"7.375"
x2=
"16"
y2=
"57.825"
spreadMethod=
"reflect"
>
<stop
offset=
"0"
style=
"stop-color:#1A6DFF"
/>
<stop
offset=
"1"
style=
"stop-color:#C822FF"
/>
</linearGradient>
<rect
x=
"12"
y=
"33"
style=
"fill:url(#SVGID_7__44764);"
width=
"8"
height=
"2"
/>
<linearGradient
id=
"SVGID_8__44764"
gradientUnits=
"userSpaceOnUse"
x1=
"25"
y1=
"7.375"
x2=
"25"
y2=
"57.825"
spreadMethod=
"reflect"
>
<stop
offset=
"0"
style=
"stop-color:#1A6DFF"
/>
<stop
offset=
"1"
style=
"stop-color:#C822FF"
/>
</linearGradient>
<rect
x=
"12"
y=
"17"
style=
"fill:url(#SVGID_8__44764);"
width=
"26"
height=
"2"
/>
</svg>
server/controllers/common.js
View file @
13f17297
...
...
@@ -29,11 +29,23 @@ router.get('/healthz', (req, res, next) => {
})
/**
* Administration
*/
router
.
get
([
'/a'
,
'/a/*'
],
(
req
,
res
,
next
)
=>
{
_
.
set
(
res
.
locals
,
'pageMeta.title'
,
'Admin'
)
res
.
render
(
'admin'
)
})
/**
* Create/Edit document
*/
router
.
get
([
'/e'
,
'/e/*'
],
async
(
req
,
res
,
next
)
=>
{
const
pageArgs
=
pageHelper
.
parsePath
(
req
.
path
,
{
stripExt
:
true
})
if
(
WIKI
.
config
.
lang
.
namespacing
&&
!
pageArgs
.
explicitLocale
)
{
return
res
.
redirect
(
`/e/
${
pageArgs
.
locale
}
/
${
pageArgs
.
path
}
`
)
}
_
.
set
(
res
,
'locals.siteConfig.lang'
,
pageArgs
.
locale
)
if
(
pageHelper
.
isReservedPath
(
pageArgs
.
path
))
{
...
...
@@ -76,27 +88,15 @@ router.get(['/e', '/e/*'], async (req, res, next) => {
})
/**
* Administration
*/
router
.
get
([
'/a'
,
'/a/*'
],
(
req
,
res
,
next
)
=>
{
_
.
set
(
res
.
locals
,
'pageMeta.title'
,
'Admin'
)
res
.
render
(
'admin'
)
})
/**
* Profile
*/
router
.
get
([
'/p'
,
'/p/*'
],
(
req
,
res
,
next
)
=>
{
_
.
set
(
res
.
locals
,
'pageMeta.title'
,
'User Profile'
)
res
.
render
(
'profile'
)
})
/**
* History
*/
router
.
get
([
'/h'
,
'/h/*'
],
async
(
req
,
res
,
next
)
=>
{
const
pageArgs
=
pageHelper
.
parsePath
(
req
.
path
,
{
stripExt
:
true
})
if
(
WIKI
.
config
.
lang
.
namespacing
&&
!
pageArgs
.
explicitLocale
)
{
return
res
.
redirect
(
`/h/
${
pageArgs
.
locale
}
/
${
pageArgs
.
path
}
`
)
}
_
.
set
(
res
,
'locals.siteConfig.lang'
,
pageArgs
.
locale
)
if
(
!
WIKI
.
auth
.
checkAccess
(
req
.
user
,
[
'read:pages'
],
pageArgs
))
{
...
...
@@ -120,11 +120,23 @@ router.get(['/h', '/h/*'], async (req, res, next) => {
})
/**
* Profile
*/
router
.
get
([
'/p'
,
'/p/*'
],
(
req
,
res
,
next
)
=>
{
_
.
set
(
res
.
locals
,
'pageMeta.title'
,
'User Profile'
)
res
.
render
(
'profile'
)
})
/**
* Source
*/
router
.
get
([
'/s'
,
'/s/*'
],
async
(
req
,
res
,
next
)
=>
{
const
pageArgs
=
pageHelper
.
parsePath
(
req
.
path
,
{
stripExt
:
true
})
if
(
WIKI
.
config
.
lang
.
namespacing
&&
!
pageArgs
.
explicitLocale
)
{
return
res
.
redirect
(
`/s/
${
pageArgs
.
locale
}
/
${
pageArgs
.
path
}
`
)
}
_
.
set
(
res
,
'locals.siteConfig.lang'
,
pageArgs
.
locale
)
if
(
!
WIKI
.
auth
.
checkAccess
(
req
.
user
,
[
'read:pages'
],
pageArgs
))
{
...
...
@@ -155,6 +167,10 @@ router.get('/*', async (req, res, next) => {
const
isPage
=
(
stripExt
||
pageArgs
.
path
.
indexOf
(
'.'
)
===
-
1
)
if
(
isPage
)
{
if
(
WIKI
.
config
.
lang
.
namespacing
&&
!
pageArgs
.
explicitLocale
)
{
return
res
.
redirect
(
`/
${
pageArgs
.
locale
}
/
${
pageArgs
.
path
}
`
)
}
if
(
!
WIKI
.
auth
.
checkAccess
(
req
.
user
,
[
'read:pages'
],
pageArgs
))
{
_
.
set
(
res
.
locals
,
'pageMeta.title'
,
'Unauthorized'
)
return
res
.
status
(
403
).
render
(
'unauthorized'
,
{
action
:
'view'
})
...
...
server/graph/resolvers/page.js
View file @
13f17297
...
...
@@ -42,6 +42,18 @@ module.exports = {
'createdAt'
,
'updatedAt'
])
},
async
single
(
obj
,
args
,
context
,
info
)
{
let
page
=
await
WIKI
.
models
.
pages
.
getPageFromDb
(
args
.
id
)
if
(
page
)
{
return
{
...
page
,
locale
:
page
.
localeCode
,
editor
:
page
.
editorKey
}
}
else
{
throw
new
WIKI
.
Error
.
PageNotFound
()
}
}
},
PageMutation
:
{
...
...
server/graph/schemas/page.graphql
View file @
13f17297
...
...
@@ -28,6 +28,10 @@ type PageQuery {
):
PageSearchResponse
!
@
auth
(
requires
:
[
"
manage
:
system
"
,
"
read
:
pages
"
])
list
:
[
PageListItem
!]!
@
auth
(
requires
:
[
"
manage
:
system
"
])
single
(
id
:
Int
!
):
Page
@
auth
(
requires
:
[
"
manage
:
pages
"
,
"
delete
:
pages
"
,
"
manage
:
system
"
])
}
# -----------------------------------------------
...
...
@@ -80,6 +84,29 @@ type PageResponse {
type
Page
{
id
:
Int
!
path
:
String
!
hash
:
String
!
title
:
String
!
description
:
String
!
isPrivate
:
Boolean
!
isPublished
:
Boolean
!
privateNS
:
String
publishStartDate
:
Date
!
publishEndDate
:
String
!
content
:
String
!
render
:
String
toc
:
String
contentType
:
String
!
createdAt
:
Date
!
updatedAt
:
Date
!
editor
:
String
!
locale
:
String
!
authorId
:
Int
!
authorName
:
String
!
authorEmail
:
String
!
creatorId
:
Int
!
creatorName
:
String
!
creatorEmail
:
String
!
}
type
PageHistory
{
...
...
server/helpers/error.js
View file @
13f17297
...
...
@@ -125,6 +125,10 @@ module.exports = {
message
:
'Cannot create this page because an entry already exists at the same path.'
,
code
:
6002
}),
PageNotFound
:
CustomError
(
'PageNotFound'
,
{
message
:
'This page does not exist.'
,
code
:
6003
}),
SearchActivationFailed
:
CustomError
(
'SearchActivationFailed'
,
{
message
:
'Search Engine activation failed.'
,
code
:
4002
...
...
server/helpers/page.js
View file @
13f17297
...
...
@@ -16,7 +16,8 @@ module.exports = {
locale
:
WIKI
.
config
.
lang
.
code
,
path
:
'home'
,
private
:
false
,
privateNS
:
''
privateNS
:
''
,
explicitLocale
:
false
}
// Clean Path
...
...
@@ -31,6 +32,7 @@ module.exports = {
}
if
(
localeSegmentRegex
.
test
(
pathParts
[
0
]))
{
pathObj
.
locale
=
pathParts
[
0
]
pathObj
.
explicitLocale
=
true
pathParts
.
shift
()
}
...
...
server/master.js
View file @
13f17297
...
...
@@ -104,6 +104,7 @@ module.exports = async () => {
// View accessible data
// ----------------------------------------
app
.
locals
.
analyticsCode
=
{}
app
.
locals
.
basedir
=
WIKI
.
ROOTPATH
app
.
locals
.
config
=
WIKI
.
config
app
.
locals
.
pageMeta
=
{
...
...
server/models/pages.js
View file @
13f17297
...
...
@@ -350,23 +350,23 @@ module.exports = class Page extends Model {
'pages.path'
:
opts
.
path
,
'pages.localeCode'
:
opts
.
locale
})
.
andWhere
(
builder
=>
{
if
(
queryModeID
)
return
builder
.
where
({
'pages.isPublished'
:
true
}).
orWhere
({
'pages.isPublished'
:
false
,
'pages.authorId'
:
opts
.
userId
})
})
.
andWhere
(
builder
=>
{
if
(
queryModeID
)
return
if
(
opts
.
isPrivate
)
{
builder
.
where
({
'pages.isPrivate'
:
true
,
'pages.privateNS'
:
opts
.
privateNS
})
}
else
{
builder
.
where
({
'pages.isPrivate'
:
false
})
}
})
//
.andWhere(builder => {
//
if (queryModeID) return
//
builder.where({
//
'pages.isPublished': true
//
}).orWhere({
//
'pages.isPublished': false,
//
'pages.authorId': opts.userId
//
})
//
})
//
.andWhere(builder => {
//
if (queryModeID) return
//
if (opts.isPrivate) {
//
builder.where({ 'pages.isPrivate': true, 'pages.privateNS': opts.privateNS })
//
} else {
//
builder.where({ 'pages.isPrivate': false })
//
}
//
})
.
first
()
}
...
...
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