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
5a7fd2d7
Commit
5a7fd2d7
authored
Sep 01, 2019
by
Nick
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: tags UI (wip) + save tags from page
parent
8e80b747
Hide whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
326 additions
and
4 deletions
+326
-4
client-app.js
client/client-app.js
+2
-1
admin-analytics.vue
client/components/admin/admin-analytics.vue
+10
-0
admin-auth.vue
client/components/admin/admin-auth.vue
+14
-1
admin-dashboard.vue
client/components/admin/admin-dashboard.vue
+2
-2
admin-dev-flags.vue
client/components/admin/admin-dev-flags.vue
+2
-0
admin-general.vue
client/components/admin/admin-general.vue
+8
-0
admin-locale.vue
client/components/admin/admin-locale.vue
+2
-0
admin-mail.vue
client/components/admin/admin-mail.vue
+2
-0
admin-rendering.vue
client/components/admin/admin-rendering.vue
+2
-0
admin-search.vue
client/components/admin/admin-search.vue
+1
-0
admin-storage.vue
client/components/admin/admin-storage.vue
+10
-0
admin-theme.vue
client/components/admin/admin-theme.vue
+1
-0
nav-header.vue
client/components/common/nav-header.vue
+5
-0
editor-modal-properties.vue
client/components/editor/editor-modal-properties.vue
+5
-0
tags.vue
client/components/tags.vue
+167
-0
common-pages-query-tags.gql
client/graph/common/common-pages-query-tags.gql
+8
-0
common.js
server/controllers/common.js
+8
-0
page.js
server/graph/resolvers/page.js
+3
-0
page.graphql
server/graph/schemas/page.graphql
+11
-0
pages.js
server/models/pages.js
+8
-0
tags.js
server/models/tags.js
+50
-0
tags.pug
server/views/tags.pug
+5
-0
No files found.
client/client-app.js
View file @
5a7fd2d7
...
...
@@ -163,9 +163,10 @@ Vue.component('not-found', () => import(/* webpackChunkName: "not-found" */ './c
Vue
.
component
(
'page-selector'
,
()
=>
import
(
/* webpackPrefetch: true, webpackChunkName: "ui-extra" */
'./components/common/page-selector.vue'
))
Vue
.
component
(
'profile'
,
()
=>
import
(
/* webpackChunkName: "profile" */
'./components/profile.vue'
))
Vue
.
component
(
'register'
,
()
=>
import
(
/* webpackChunkName: "register" */
'./components/register.vue'
))
Vue
.
component
(
'v-card-chin'
,
()
=>
import
(
/* webpackPrefetch: true, webpackChunkName: "ui-extra" */
'./components/common/v-card-chin.vue'
))
Vue
.
component
(
'search-results'
,
()
=>
import
(
/* webpackPrefetch: true, webpackChunkName: "ui-extra" */
'./components/common/search-results.vue'
))
Vue
.
component
(
'tags'
,
()
=>
import
(
/* webpackChunkName: "tags" */
'./components/tags.vue'
))
Vue
.
component
(
'unauthorized'
,
()
=>
import
(
/* webpackChunkName: "unauthorized" */
'./components/unauthorized.vue'
))
Vue
.
component
(
'v-card-chin'
,
()
=>
import
(
/* webpackPrefetch: true, webpackChunkName: "ui-extra" */
'./components/common/v-card-chin.vue'
))
Vue
.
component
(
'welcome'
,
()
=>
import
(
/* webpackChunkName: "welcome" */
'./components/welcome.vue'
))
Vue
.
component
(
'nav-footer'
,
()
=>
import
(
/* webpackChunkName: "theme-page" */
'./themes/'
+
process
.
env
.
CURRENT_THEME
+
'/components/nav-footer.vue'
))
...
...
client/components/admin/admin-analytics.vue
View file @
5a7fd2d7
...
...
@@ -37,6 +37,15 @@
v-card.animated.fadeInUp.wait-p2s
v-toolbar(color='primary', dense, flat, dark)
.subtitle-1
{{
provider
.
title
}}
v-spacer
v-switch(
dark
color='blue lighten-5'
label='Active'
v-model='provider.isEnabled'
hide-details
inset
)
v-card-text
v-form
.analytic-provider-logo
...
...
@@ -68,6 +77,7 @@
prepend-icon='mdi-settings-box'
:hint='cfg.value.hint ? cfg.value.hint : ""'
persistent-hint
inset
)
v-textarea(
v-else-if='cfg.value.type === "string" && cfg.value.multiline'
...
...
client/components/admin/admin-auth.vue
View file @
5a7fd2d7
...
...
@@ -63,10 +63,19 @@
)
v-flex(xs12, lg9)
v-card.animated.fadeInUp.wait-p2s
v-toolbar(color='primary', dense, flat, dark)
.subtitle-1
{{
strategy
.
title
}}
v-spacer
v-switch(
dark
color='blue lighten-5'
label='Active'
v-model='strategy.isEnabled'
hide-details
inset
:disabled='strategy.key === `local`'
)
v-card-text
v-form
.authlogo
...
...
@@ -104,6 +113,7 @@
prepend-icon='mdi-settings-box'
:hint='cfg.value.hint ? cfg.value.hint : ""'
persistent-hint
inset
)
v-textarea(
v-else-if='cfg.value.type === "string" && cfg.value.multiline'
...
...
@@ -136,6 +146,7 @@
color='primary'
:hint='$t(`admin:auth.selfRegistrationHint`)'
persistent-hint
inset
)
v-switch.ml-3(
v-if='strategy.key === `local`'
...
...
@@ -145,6 +156,7 @@
color='primary'
hint='Protects against spam robots and malicious registrations.'
persistent-hint
inset
)
v-combobox.ml-3.mt-3(
:label='$t(`admin:auth.domainsWhitelist`)'
...
...
@@ -187,6 +199,7 @@
color='primary'
:hint='$t(`admin:auth.force2faHint`)'
persistent-hint
inset
)
v-card.mt-4.wiki-form.animated.fadeInUp.wait-p4s
...
...
client/components/admin/admin-dashboard.vue
View file @
5a7fd2d7
...
...
@@ -19,7 +19,7 @@
easing='easeOutQuint'
)
v-flex(xs12 md6 lg4 xl3 d-flex)
v-card.
indigo
.lighten-1.dashboard-card.animated.fadeInUp.wait-p2s(dark)
v-card.
green
.lighten-1.dashboard-card.animated.fadeInUp.wait-p2s(dark)
v-card-text
v-icon.dashboard-icon mdi-account
.overline
{{
$t
(
'admin:dashboard.users'
)
}}
...
...
@@ -30,7 +30,7 @@
easing='easeOutQuint'
)
v-flex(xs12 md6 lg4 xl3 d-flex)
v-card.indigo.lighten-
2
.dashboard-card.animated.fadeInUp.wait-p4s(dark)
v-card.indigo.lighten-
1
.dashboard-card.animated.fadeInUp.wait-p4s(dark)
v-card-text
v-icon.dashboard-icon mdi-account-group
.overline
{{
$t
(
'admin:dashboard.groups'
)
}}
...
...
client/components/admin/admin-dev-flags.vue
View file @
5a7fd2d7
...
...
@@ -23,6 +23,7 @@
persistent-hint
label='LDAP Debug'
v-model='flags.ldapdebug'
inset
)
v-divider.mt-3
v-switch.mt-3(
...
...
@@ -31,6 +32,7 @@
persistent-hint
label='SQL Query Logging'
v-model='flags.sqllog'
inset
)
</
template
>
...
...
client/components/admin/admin-general.vue
View file @
5a7fd2d7
...
...
@@ -98,6 +98,7 @@
v
-
chip
(
label
,
color
=
'white'
,
small
).
indigo
--
text
coming
soon
v
-
card
-
text
v
-
switch
(
inset
label
=
'Asset Image Optimization'
color
=
'indigo'
v
-
model
=
'config.featureTinyPNG'
...
...
@@ -118,6 +119,7 @@
v
-
divider
.
mt
-
3
v
-
switch
(
inset
label
=
'Page Ratings'
color
=
'indigo'
v
-
model
=
'config.featurePageRatings'
...
...
@@ -128,6 +130,7 @@
v
-
divider
.
mt
-
3
v
-
switch
(
inset
label
=
'Page Comments'
color
=
'indigo'
v
-
model
=
'config.featurePageComments'
...
...
@@ -138,6 +141,7 @@
v
-
divider
.
mt
-
3
v
-
switch
(
inset
label
=
'Personal Wikis'
color
=
'indigo'
v
-
model
=
'config.featurePersonalWikis'
...
...
@@ -152,6 +156,7 @@
v
-
card
-
text
v
-
alert
(
outlined
,
color
=
'red darken-2'
,
icon
=
'mdi-information-outline'
).
body
-
2
Make
sure
to
understand
the
implications
before
turning
on
/
off
a
security
feature
.
v
-
switch
.
mt
-
3
(
inset
label
=
'Block IFrame Embedding'
color
=
'red darken-2'
v
-
model
=
'config.securityIframe'
...
...
@@ -160,6 +165,7 @@
)
v
-
divider
.
mt
-
3
v
-
switch
(
inset
label
=
'Same Origin Referrer Policy'
color
=
'red darken-2'
v
-
model
=
'config.securityReferrerPolicy'
...
...
@@ -169,6 +175,7 @@
v
-
divider
.
mt
-
3
v
-
switch
(
inset
label
=
'Enforce HSTS'
color
=
'red darken-2'
v
-
model
=
'config.securityHSTS'
...
...
@@ -191,6 +198,7 @@
v-divider.mt-3
v-switch(
inset
label='
Enforce
CSP
'
color='
red
darken
-
2
'
v-model='
config
.
securityCSP
'
...
...
client/components/admin/admin-locale.vue
View file @
5a7fd2d7
...
...
@@ -40,6 +40,7 @@
v-list-item-subtitle(v-html='data.item.nativeName')
v-divider.mt-3
v-switch(
inset
v-model='autoUpdate'
:label='$t("admin:locale.autoUpdate.label")'
color='primary'
...
...
@@ -52,6 +53,7 @@
v-toolbar-title.subtitle-1
{{
$t
(
'admin:locale.namespacing'
)
}}
v-card-text
v-switch(
inset
v-model='namespacing'
:label='$t("admin:locale.namespaces.label")'
color='primary'
...
...
client/components/admin/admin-mail.vue
View file @
5a7fd2d7
...
...
@@ -64,6 +64,7 @@
persistent-hint
:hint='$t(`admin:mail.smtpTLSHint`)'
prepend-icon='mdi-security-network'
inset
)
v-text-field.mt-3(
outlined
...
...
@@ -94,6 +95,7 @@
:label='$t(`admin:mail.dkimUse`)'
color='primary'
prepend-icon='mdi-key'
inset
)
v-text-field(
outlined
...
...
client/components/admin/admin-rendering.vue
View file @
5a7fd2d7
...
...
@@ -82,6 +82,7 @@
label='Enabled'
v-model='currentRenderer.isEnabled'
hide-details
inset
)
v-card-text.pb-4.pt-2.pl-4
.overline.my-5 Rendering Module Configuration
...
...
@@ -106,6 +107,7 @@
color='primary'
:hint='cfg.value.hint ? cfg.value.hint : ""'
persistent-hint
inset
)
v-text-field(
v-else
...
...
client/components/admin/admin-search.vue
View file @
5a7fd2d7
...
...
@@ -69,6 +69,7 @@
prepend-icon='mdi-settings-box'
:hint='cfg.value.hint ? cfg.value.hint : ""'
persistent-hint
inset
)
v-textarea(
v-else-if='cfg.value.type === "string" && cfg.value.multiline'
...
...
client/components/admin/admin-storage.vue
View file @
5a7fd2d7
...
...
@@ -80,6 +80,15 @@
v
-
card
.
wiki
-
form
.
animated
.
fadeInUp
.
wait
-
p2s
v
-
toolbar
(
color
=
'primary'
,
dense
,
flat
,
dark
)
.
subtitle
-
1
{{
target
.
title
}}
v
-
spacer
v
-
switch
(
dark
color
=
'blue lighten-5'
label
=
'Active'
v
-
model
=
'target.isEnabled'
hide
-
details
inset
)
v
-
card
-
text
v
-
form
.
targetlogo
...
...
@@ -115,6 +124,7 @@
prepend
-
icon
=
'mdi-settings-box'
:
hint
=
'cfg.value.hint ? cfg.value.hint : ""'
persistent
-
hint
inset
)
v
-
textarea
(
v
-
else
-
if
=
'cfg.value.type === "string" && cfg.value.multiline'
...
...
client/components/admin/admin-theme.vue
View file @
5a7fd2d7
...
...
@@ -44,6 +44,7 @@
)
v-divider.mt-3
v-switch(
inset
v-model='darkMode'
:label='$t(`admin:theme.darkMode`)'
color='primary'
...
...
client/components/common/nav-header.vue
View file @
5a7fd2d7
...
...
@@ -125,6 +125,11 @@
//- v-btn(depressed, color='grey darken-3', block)
//- v-icon(left) mdi-cached
//- span Reset
v-tooltip(bottom, v-if='isAuthenticated && isAdmin')
template(v-slot:activator='{ on }')
v-btn.ml-2.mr-0(icon, v-on='on', href='/t')
v-icon(color='grey') mdi-tag-multiple
span Browse Tags
v-flex(xs6, md4)
v-toolbar.nav-header-inner.pr-4(color='black', dark, flat)
v-spacer
...
...
client/components/editor/editor-modal-properties.vue
View file @
5a7fd2d7
...
...
@@ -90,6 +90,7 @@
:hint='$t(`editor:props.publishToggleHint`)'
persistent-hint
disabled
inset
)
v-divider
v-card-text.grey.pt-5(:class='darkMode ? `darken-3-d3` : `lighten-5`')
...
...
@@ -190,6 +191,7 @@
:hint='$t(`editor:props.allowCommentsHint`)'
persistent-hint
disabled
inset
)
v-switch(
:label='$t(`editor:props.allowRatings`)'
...
...
@@ -198,6 +200,7 @@
:hint='$t(`editor:props.allowRatingsHint`)'
persistent-hint
disabled
inset
)
v-switch(
:label='$t(`editor:props.displayAuthor`)'
...
...
@@ -206,6 +209,7 @@
:hint='$t(`editor:props.displayAuthorHint`)'
persistent-hint
disabled
inset
)
v-switch(
:label='$t(`editor:props.displaySharingBar`)'
...
...
@@ -214,6 +218,7 @@
:hint='$t(`editor:props.displaySharingBarHint`)'
persistent-hint
disabled
inset
)
page-selector(mode='create', v-model='pageSelectorShown', :path='path', :locale='locale', :open-handler='setPath')
...
...
client/components/tags.vue
0 → 100644
View file @
5a7fd2d7
<
template
lang=
'pug'
>
v-app(:dark='darkMode').tags
nav-header
v-navigation-drawer.pb-0.elevation-1(app, fixed, clipped, :right='$vuetify.rtl', permanent, width='300')
vue-scroll(:ops='scrollStyle')
v-list(dense, nav)
v-list-item(href='/')
v-list-item-icon: v-icon mdi-home
v-list-item-title
{{
$t
(
'common:header.home'
)
}}
template(v-for='(tags, groupName) in tagsGrouped')
v-divider.my-2
v-subheader.pl-4(:key='`tagGroup-` + groupName')
{{
groupName
}}
v-list-item(v-for='tag of tags', @click='toggleTag(tag.tag)', :key='`tag-` + tag.tag')
v-list-item-icon
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-toolbar(color='primary', dark, flat, height='58')
template(v-if='selection.length > 0')
.overline.mr-3.animated.fadeInLeft Current Selection
v-chip.mr-3.primary--text(
v-for='tag of tagsSelected'
color='white'
close
@click:close='toggleTag(tag.tag)'
)
{{
tag
.
title
}}
v-spacer
v-btn.animated.fadeIn(
small
outlined
color='blue lighten-4'
rounded
@click='selection = []'
)
v-icon(left) mdi-close
span Clear Selection
template(v-else)
v-icon.mr-3.animated.fadeInRight mdi-arrow-left
.overline.animated.fadeInRight Select one or more tags
v-toolbar(color='grey lighten-4', flat, height='58')
v-text-field.tags-search(
label='Search within results...'
solo
hide-details
flat
rounded
single-line
height='40'
prepend-icon='mdi-file-document-box-search-outline'
append-icon='mdi-arrow-right'
)
v-divider.mx-3(vertical)
.overline Order By
v-select.ml-2(
:items='orderByItems'
v-model='orderBy'
background-color='white'
hide-details
label='Order By'
rounded
single-line
dense
height='40'
style='max-width: 250px;'
)
v-divider.mx-3(vertical)
v-btn-toggle(v-model='displayStyle', rounded, mandatory)
v-btn(text, height='40'): v-icon(small) mdi-view-list
v-btn(text, height='40'): v-icon(small) mdi-cards-variant
v-btn(text, height='40'): v-icon(small) mdi-format-align-justify
v-divider
nav-footer
notify
search-results
</
template
>
<
script
>
import
{
get
}
from
'vuex-pathify'
import
_
from
'lodash'
import
tagsQuery
from
'gql/common/common-pages-query-tags.gql'
export
default
{
data
()
{
return
{
tags
:
[],
selection
:
[],
displayStyle
:
0
,
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'
}
],
scrollStyle
:
{
vuescroll
:
{},
scrollPanel
:
{
initialScrollY
:
0
,
initialScrollX
:
0
,
scrollingX
:
false
,
easing
:
'easeOutQuad'
,
speed
:
1000
,
verticalNativeBarPos
:
this
.
$vuetify
.
rtl
?
`left`
:
`right`
},
rail
:
{
gutterOfEnds
:
'2px'
},
bar
:
{
onlyShowBarOnScroll
:
false
,
background
:
'#CCC'
,
hoverStyle
:
{
background
:
'#999'
}
}
}
}
},
computed
:
{
darkMode
:
get
(
'site/dark'
),
tagsGrouped
()
{
return
_
.
groupBy
(
this
.
tags
,
t
=>
t
.
title
.
charAt
(
0
).
toUpperCase
())
},
tagsSelected
()
{
return
_
.
filter
(
this
.
tags
,
t
=>
_
.
includes
(
this
.
selection
,
t
.
tag
))
}
},
created
()
{
this
.
$store
.
commit
(
'page/SET_MODE'
,
'tags'
)
},
methods
:
{
toggleTag
(
tag
)
{
if
(
_
.
includes
(
this
.
selection
,
tag
))
{
this
.
selection
=
_
.
without
(
this
.
selection
,
tag
)
}
else
{
this
.
selection
.
push
(
tag
)
}
},
isSelected
(
tag
)
{
return
_
.
includes
(
this
.
selection
,
tag
)
}
},
apollo
:
{
tags
:
{
query
:
tagsQuery
,
fetchPolicy
:
'cache-and-network'
,
update
:
(
data
)
=>
_
.
cloneDeep
(
data
.
pages
.
tags
),
watchLoading
(
isLoading
)
{
this
.
$store
.
commit
(
`loading
${
isLoading
?
'Start'
:
'Stop'
}
`
,
'tags-refresh'
)
}
}
}
}
</
script
>
<
style
lang=
'scss'
>
.tags-search
{
.v-input__control
{
min-height
:
initial
!
important
;
}
.v-input__prepend-outer
{
margin-top
:
8px
!
important
;
}
}
</
style
>
client/graph/common/common-pages-query-tags.gql
0 → 100644
View file @
5a7fd2d7
query
{
pages
{
tags
{
tag
title
}
}
}
server/controllers/common.js
View file @
5a7fd2d7
...
...
@@ -166,6 +166,14 @@ router.get(['/s', '/s/*'], async (req, res, next) => {
})
/**
* Tags
*/
router
.
get
([
'/t'
,
'/t/*'
],
(
req
,
res
,
next
)
=>
{
_
.
set
(
res
.
locals
,
'pageMeta.title'
,
'Tags'
)
res
.
render
(
'tags'
)
})
/**
* View document / asset
*/
router
.
get
(
'/*'
,
async
(
req
,
res
,
next
)
=>
{
...
...
server/graph/resolvers/page.js
View file @
5a7fd2d7
...
...
@@ -76,6 +76,9 @@ module.exports = {
}
else
{
throw
new
WIKI
.
Error
.
PageNotFound
()
}
},
async
tags
(
obj
,
args
,
context
,
info
)
{
return
WIKI
.
models
.
tags
.
query
().
orderBy
(
'tag'
,
'asc'
)
}
},
PageMutation
:
{
...
...
server/graph/schemas/page.graphql
View file @
5a7fd2d7
...
...
@@ -36,6 +36,8 @@ type PageQuery {
single
(
id
:
Int
!
):
Page
@
auth
(
requires
:
[
"
manage
:
pages
"
,
"
delete
:
pages
"
,
"
manage
:
system
"
])
tags
:
[
PageTag
]!
@
auth
(
requires
:
[
"
manage
:
system
"
,
"
read
:
pages
"
])
}
# -----------------------------------------------
...
...
@@ -109,6 +111,7 @@ type Page {
privateNS
:
String
publishStartDate
:
Date
!
publishEndDate
:
String
!
tags
:
[
PageTag
]!
content
:
String
!
render
:
String
toc
:
String
...
...
@@ -125,6 +128,14 @@ type Page {
creatorEmail
:
String
!
}
type
PageTag
{
id
:
Int
!
tag
:
String
!
title
:
String
createdAt
:
Date
!
updatedAt
:
Date
!
}
type
PageHistory
{
versionId
:
Int
!
authorId
:
Int
!
...
...
server/models/pages.js
View file @
5a7fd2d7
...
...
@@ -210,6 +210,11 @@ module.exports = class Page extends Model {
isPrivate
:
opts
.
isPrivate
})
// -> Save Tags
if
(
opts
.
tags
.
length
>
0
)
{
await
WIKI
.
models
.
tags
.
associateTags
({
tags
:
opts
.
tags
,
page
})
}
// -> Render page to HTML
await
WIKI
.
models
.
pages
.
renderPage
(
page
)
...
...
@@ -260,6 +265,9 @@ module.exports = class Page extends Model {
isPrivate
:
ogPage
.
isPrivate
})
// -> Save Tags
await
WIKI
.
models
.
tags
.
associateTags
({
tags
:
opts
.
tags
,
page
})
// -> Render page to HTML
await
WIKI
.
models
.
pages
.
renderPage
(
page
)
...
...
server/models/tags.js
View file @
5a7fd2d7
const
Model
=
require
(
'objection'
).
Model
const
_
=
require
(
'lodash'
)
/* global WIKI */
/**
* Tags model
...
...
@@ -46,4 +49,51 @@ module.exports = class Tag extends Model {
this
.
createdAt
=
new
Date
().
toISOString
()
this
.
updatedAt
=
new
Date
().
toISOString
()
}
static
async
associateTags
({
tags
,
page
})
{
let
existingTags
=
await
WIKI
.
models
.
tags
.
query
().
column
(
'id'
,
'tag'
)
// Create missing tags
const
newTags
=
_
.
filter
(
tags
,
t
=>
!
_
.
some
(
existingTags
,
[
'tag'
,
t
])).
map
(
t
=>
({
tag
:
t
,
title
:
t
}))
if
(
newTags
.
length
>
0
)
{
if
(
WIKI
.
config
.
db
.
type
===
'postgres'
)
{
const
createdTags
=
await
WIKI
.
models
.
tags
.
query
().
insert
(
newTags
)
existingTags
=
_
.
concat
(
existingTags
,
createdTags
)
}
else
{
for
(
const
newTag
of
newTags
)
{
const
createdTag
=
await
WIKI
.
models
.
tags
.
query
().
insert
(
newTag
)
existingTags
.
push
(
createdTag
)
}
}
}
// Fetch current page tags
const
targetTags
=
_
.
filter
(
existingTags
,
t
=>
_
.
includes
(
tags
,
t
.
tag
))
const
currentTags
=
await
page
.
$relatedQuery
(
'tags'
)
// Tags to relate
const
tagsToRelate
=
_
.
differenceBy
(
targetTags
,
currentTags
,
'id'
)
if
(
tagsToRelate
.
length
>
0
)
{
if
(
WIKI
.
config
.
db
.
type
===
'postgres'
)
{
await
page
.
$relatedQuery
(
'tags'
).
relate
(
tagsToRelate
)
}
else
{
for
(
const
tag
of
tagsToRelate
)
{
await
page
.
$relatedQuery
(
'tags'
).
relate
(
tag
)
}
}
}
// Tags to unrelate
const
tagsToUnrelate
=
_
.
differenceBy
(
currentTags
,
targetTags
,
'id'
)
if
(
tagsToUnrelate
.
length
>
0
)
{
await
page
.
$relatedQuery
(
'tags'
).
unrelate
().
whereIn
(
'tags.id'
,
_
.
map
(
tagsToUnrelate
,
'id'
))
}
}
}
server/views/tags.pug
0 → 100644
View file @
5a7fd2d7
extends master.pug
block body
#root
tags
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