feat: save page
This commit is contained in:
parent
c7b675bb1c
commit
cb84df7a53
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -2,7 +2,6 @@
|
|||||||
"eslint.enable": true,
|
"eslint.enable": true,
|
||||||
"eslint.autoFixOnSave": true,
|
"eslint.autoFixOnSave": true,
|
||||||
"puglint.enable": true,
|
"puglint.enable": true,
|
||||||
"standard.enable": false,
|
|
||||||
"editor.formatOnSave": false,
|
"editor.formatOnSave": false,
|
||||||
"editor.tabSize": 2,
|
"editor.tabSize": 2,
|
||||||
"eslint.validate": [
|
"eslint.validate": [
|
||||||
|
@ -21,16 +21,27 @@
|
|||||||
:size='60'
|
:size='60'
|
||||||
color='#FFF'
|
color='#FFF'
|
||||||
)
|
)
|
||||||
.subheading Processing
|
.subheading {{ $t('editor:save.processing') }}
|
||||||
.caption.blue--text.text--lighten-3 Please wait...
|
.caption.blue--text.text--lighten-3 {{ $t('editor:save.pleaseWait') }}
|
||||||
|
|
||||||
|
v-snackbar(
|
||||||
|
:color='notification.style'
|
||||||
|
bottom,
|
||||||
|
right,
|
||||||
|
multi-line,
|
||||||
|
v-model='notificationState'
|
||||||
|
)
|
||||||
|
.text-xs-left
|
||||||
|
v-icon.mr-3(dark) {{ notification.icon }}
|
||||||
|
span {{ notification.message }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import { get } from 'vuex-pathify'
|
import { get, sync } from 'vuex-pathify'
|
||||||
import { AtomSpinner } from 'epic-spinners'
|
import { AtomSpinner } from 'epic-spinners'
|
||||||
|
|
||||||
import savePageMutation from 'gql/editor/save.gql'
|
import createPageMutation from 'gql/editor/create.gql'
|
||||||
|
|
||||||
import editorStore from '@/store/editor'
|
import editorStore from '@/store/editor'
|
||||||
|
|
||||||
@ -51,7 +62,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
mode: get('editor/mode')
|
mode: get('editor/mode'),
|
||||||
|
notification: get('notification'),
|
||||||
|
notificationState: sync('notification@isActive')
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.mode === 'create') {
|
if (this.mode === 'create') {
|
||||||
@ -77,12 +90,35 @@ export default {
|
|||||||
},
|
},
|
||||||
async save() {
|
async save() {
|
||||||
this.showProgressDialog('saving')
|
this.showProgressDialog('saving')
|
||||||
// const resp = await this.$apollo.mutate({
|
if (this.$store.get('editor/mode') === 'create') {
|
||||||
// mutation: savePageMutation,
|
const resp = await this.$apollo.mutate({
|
||||||
// variables: {
|
mutation: createPageMutation,
|
||||||
|
variables: {
|
||||||
|
description: this.$store.get('editor/description'),
|
||||||
|
editor: 'markdown',
|
||||||
|
locale: this.$store.get('editor/locale'),
|
||||||
|
isPublished: this.$store.get('editor/isPublished'),
|
||||||
|
path: this.$store.get('editor/path'),
|
||||||
|
publishEndDate: this.$store.get('editor/publishEndDate'),
|
||||||
|
publishStartDate: this.$store.get('editor/publishStartDate'),
|
||||||
|
tags: this.$store.get('editor/tags'),
|
||||||
|
title: this.$store.get('editor/title')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (_.get(resp, 'data.pages.create.responseResult.succeeded')) {
|
||||||
|
this.$store.commit('showNotification', {
|
||||||
|
message: this.$t('editor:save.success'),
|
||||||
|
style: 'success',
|
||||||
|
icon: 'check'
|
||||||
|
})
|
||||||
|
this.$store.set('editor/mode', 'update')
|
||||||
|
} else {
|
||||||
|
|
||||||
// }
|
}
|
||||||
// })
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
this.hideProgressDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,30 +32,46 @@
|
|||||||
counter='255'
|
counter='255'
|
||||||
v-model='description'
|
v-model='description'
|
||||||
)
|
)
|
||||||
v-text-field(
|
|
||||||
outline
|
|
||||||
background-color='grey lighten-2'
|
|
||||||
label='Path'
|
|
||||||
prefix='/'
|
|
||||||
append-icon='folder'
|
|
||||||
v-model='path'
|
|
||||||
)
|
|
||||||
v-divider
|
v-divider
|
||||||
v-card-text
|
v-card-text.grey.lighten-5
|
||||||
v-subheader.pl-0 Tags
|
v-subheader.pl-0 Path & Categorization
|
||||||
|
v-container.pa-0(fluid, grid-list-lg)
|
||||||
|
v-layout(row, wrap)
|
||||||
|
v-flex(xs12, md2)
|
||||||
|
v-select(
|
||||||
|
outline
|
||||||
|
background-color='grey lighten-2'
|
||||||
|
label='Locale'
|
||||||
|
suffix='/'
|
||||||
|
:items='namespaces'
|
||||||
|
v-model='locale'
|
||||||
|
hide-details
|
||||||
|
)
|
||||||
|
v-flex(xs12, md10)
|
||||||
|
v-text-field(
|
||||||
|
outline
|
||||||
|
background-color='grey lighten-2'
|
||||||
|
label='Path'
|
||||||
|
append-icon='folder'
|
||||||
|
v-model='path'
|
||||||
|
hint='Do not include any leading or trailing slashes.'
|
||||||
|
persistent-hint
|
||||||
|
@click:append='showPathSelector'
|
||||||
|
)
|
||||||
v-combobox(
|
v-combobox(
|
||||||
background-color='grey lighten-2'
|
background-color='grey lighten-2'
|
||||||
chips
|
chips
|
||||||
deletable-chips
|
deletable-chips
|
||||||
hide-details
|
|
||||||
label='Tags'
|
label='Tags'
|
||||||
outline
|
outline
|
||||||
multiple
|
multiple
|
||||||
v-model='tags'
|
v-model='tags'
|
||||||
single-line
|
single-line
|
||||||
|
hint='Use tags to categorize your pages and make them easier to find.'
|
||||||
|
persistent-hint
|
||||||
)
|
)
|
||||||
v-divider
|
v-divider
|
||||||
v-card-text.pb-5
|
v-card-text.pb-5.grey.lighten-4
|
||||||
v-subheader.pl-0 Publishing State
|
v-subheader.pl-0 Publishing State
|
||||||
v-container.pa-0(fluid, grid-list-lg)
|
v-container.pa-0(fluid, grid-list-lg)
|
||||||
v-layout(row, wrap)
|
v-layout(row, wrap)
|
||||||
@ -64,6 +80,8 @@
|
|||||||
label='Published'
|
label='Published'
|
||||||
v-model='isPublished'
|
v-model='isPublished'
|
||||||
color='primary'
|
color='primary'
|
||||||
|
hint='Unpublished pages can still be seen by users having write permissions.'
|
||||||
|
persistent-hint
|
||||||
)
|
)
|
||||||
v-flex(xs12, md4)
|
v-flex(xs12, md4)
|
||||||
v-dialog(
|
v-dialog(
|
||||||
@ -165,6 +183,7 @@ export default {
|
|||||||
isShown: false,
|
isShown: false,
|
||||||
isPublishStartShown: false,
|
isPublishStartShown: false,
|
||||||
isPublishEndShown: false,
|
isPublishEndShown: false,
|
||||||
|
namespaces: ['en'],
|
||||||
tourSteps: [
|
tourSteps: [
|
||||||
{
|
{
|
||||||
target: '.dialog-header',
|
target: '.dialog-header',
|
||||||
@ -176,6 +195,7 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
title: sync('editor/title'),
|
title: sync('editor/title'),
|
||||||
description: sync('editor/description'),
|
description: sync('editor/description'),
|
||||||
|
locale: sync('editor/locale'),
|
||||||
tags: sync('editor/tags'),
|
tags: sync('editor/tags'),
|
||||||
path: sync('editor/path'),
|
path: sync('editor/path'),
|
||||||
isPublished: sync('editor/isPublished'),
|
isPublished: sync('editor/isPublished'),
|
||||||
@ -193,6 +213,13 @@ export default {
|
|||||||
close() {
|
close() {
|
||||||
this.isShown = false
|
this.isShown = false
|
||||||
this.$parent.$parent.closeModal()
|
this.$parent.$parent.closeModal()
|
||||||
|
},
|
||||||
|
showPathSelector() {
|
||||||
|
this.$store.commit('showNotification', {
|
||||||
|
message: 'Coming soon!',
|
||||||
|
style: 'purple',
|
||||||
|
icon: 'directions_boat'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
15
client/graph/editor/create.gql
Normal file
15
client/graph/editor/create.gql
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
mutation ($description: String, $editor: String, $isPublished: Boolean!, $locale: String!, $path: String!, $publishEndDate: Date, $publishStartDate: Date, $tags: [String], $title: String!) {
|
||||||
|
pages {
|
||||||
|
create(description: $description, editor: $editor, isPublished: $isPublished, locale: $locale, path: $path, publishEndDate: $publishEndDate, publishStartDate: $publishStartDate, tags: $tags, title: $title) {
|
||||||
|
responseResult {
|
||||||
|
succeeded
|
||||||
|
errorCode
|
||||||
|
slug
|
||||||
|
message
|
||||||
|
}
|
||||||
|
page {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +0,0 @@
|
|||||||
mutation {
|
|
||||||
page {
|
|
||||||
create {
|
|
||||||
page
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +1,32 @@
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
import pathify from 'vuex-pathify'
|
import pathify from 'vuex-pathify' // eslint-disable-line import/no-duplicates
|
||||||
|
import { make } from 'vuex-pathify' // eslint-disable-line import/no-duplicates
|
||||||
|
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
loadingStack: [],
|
||||||
|
notification: {
|
||||||
|
message: '',
|
||||||
|
style: 'primary',
|
||||||
|
icon: 'cached',
|
||||||
|
isActive: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
strict: process.env.NODE_ENV !== 'production',
|
strict: process.env.NODE_ENV !== 'production',
|
||||||
plugins: [
|
plugins: [
|
||||||
pathify.plugin
|
pathify.plugin
|
||||||
],
|
],
|
||||||
state: {
|
state,
|
||||||
loadingStack: [],
|
|
||||||
notification: {
|
|
||||||
message: '',
|
|
||||||
style: 'primary',
|
|
||||||
icon: 'cached',
|
|
||||||
isActive: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getters: {
|
getters: {
|
||||||
isLoading: state => { return state.loadingStack.length > 0 }
|
isLoading: state => { return state.loadingStack.length > 0 }
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
|
...make.mutations(state),
|
||||||
loadingStart (state, stackName) {
|
loadingStart (state, stackName) {
|
||||||
state.loadingStack = _.union(state.loadingStack, [stackName])
|
state.loadingStack = _.union(state.loadingStack, [stackName])
|
||||||
},
|
},
|
||||||
|
@ -150,16 +150,16 @@ exports.up = knex => {
|
|||||||
table.integer('authorId').unsigned().references('id').inTable('users')
|
table.integer('authorId').unsigned().references('id').inTable('users')
|
||||||
})
|
})
|
||||||
.table('pages', table => {
|
.table('pages', table => {
|
||||||
table.string('editor').references('key').inTable('editors')
|
table.string('editorKey').references('key').inTable('editors')
|
||||||
table.string('locale', 2).references('code').inTable('locales')
|
table.string('localeCode', 2).references('code').inTable('locales')
|
||||||
table.integer('authorId').unsigned().references('id').inTable('users')
|
table.integer('authorId').unsigned().references('id').inTable('users')
|
||||||
})
|
})
|
||||||
.table('users', table => {
|
.table('users', table => {
|
||||||
table.string('provider').references('key').inTable('authentication').notNullable().defaultTo('local')
|
table.string('providerKey').references('key').inTable('authentication').notNullable().defaultTo('local')
|
||||||
table.string('locale', 2).references('code').inTable('locales').notNullable().defaultTo('en')
|
table.string('localeCode', 2).references('code').inTable('locales').notNullable().defaultTo('en')
|
||||||
table.string('defaultEditor').references('key').inTable('editors').notNullable().defaultTo('markdown')
|
table.string('defaultEditor').references('key').inTable('editors').notNullable().defaultTo('markdown')
|
||||||
|
|
||||||
table.unique(['provider', 'email'])
|
table.unique(['providerKey', 'email'])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,11 +49,19 @@ module.exports = class Page extends Model {
|
|||||||
to: 'users.id'
|
to: 'users.id'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
editor: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: require('./editors'),
|
||||||
|
join: {
|
||||||
|
from: 'pages.editorKey',
|
||||||
|
to: 'editors.key'
|
||||||
|
}
|
||||||
|
},
|
||||||
locale: {
|
locale: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: require('./locales'),
|
modelClass: require('./locales'),
|
||||||
join: {
|
join: {
|
||||||
from: 'users.locale',
|
from: 'pages.localeCode',
|
||||||
to: 'locales.code'
|
to: 'locales.code'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,13 +23,11 @@ module.exports = class User extends Model {
|
|||||||
id: {type: 'integer'},
|
id: {type: 'integer'},
|
||||||
email: {type: 'string', format: 'email'},
|
email: {type: 'string', format: 'email'},
|
||||||
name: {type: 'string', minLength: 1, maxLength: 255},
|
name: {type: 'string', minLength: 1, maxLength: 255},
|
||||||
provider: {type: 'string', minLength: 1, maxLength: 255},
|
|
||||||
providerId: {type: 'number'},
|
providerId: {type: 'number'},
|
||||||
password: {type: 'string'},
|
password: {type: 'string'},
|
||||||
role: {type: 'string', enum: ['admin', 'guest', 'user']},
|
role: {type: 'string', enum: ['admin', 'guest', 'user']},
|
||||||
tfaIsActive: {type: 'boolean', default: false},
|
tfaIsActive: {type: 'boolean', default: false},
|
||||||
tfaSecret: {type: 'string'},
|
tfaSecret: {type: 'string'},
|
||||||
locale: {type: 'string'},
|
|
||||||
jobTitle: {type: 'string'},
|
jobTitle: {type: 'string'},
|
||||||
location: {type: 'string'},
|
location: {type: 'string'},
|
||||||
pictureUrl: {type: 'string'},
|
pictureUrl: {type: 'string'},
|
||||||
@ -52,6 +50,30 @@ module.exports = class User extends Model {
|
|||||||
},
|
},
|
||||||
to: 'groups.id'
|
to: 'groups.id'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
provider: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: require('./authentication'),
|
||||||
|
join: {
|
||||||
|
from: 'users.providerKey',
|
||||||
|
to: 'authentication.key'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
defaultEditor: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: require('./editors'),
|
||||||
|
join: {
|
||||||
|
from: 'users.editorKey',
|
||||||
|
to: 'editors.key'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
locale: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: require('./locales'),
|
||||||
|
join: {
|
||||||
|
from: 'users.localeCode',
|
||||||
|
to: 'locales.code'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,35 +11,44 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
PageQuery: {
|
PageQuery: {
|
||||||
async list(obj, args, context, info) {
|
async list(obj, args, context, info) {
|
||||||
return WIKI.db.groups.query().select(
|
return WIKI.db.pages.query().select(
|
||||||
'groups.*',
|
'pages.*',
|
||||||
WIKI.db.groups.relatedQuery('users').count().as('userCount')
|
WIKI.db.pages.relatedQuery('users').count().as('userCount')
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
async single(obj, args, context, info) {
|
async single(obj, args, context, info) {
|
||||||
return WIKI.db.groups.query().findById(args.id)
|
return WIKI.db.pages.query().findById(args.id)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
PageMutation: {
|
PageMutation: {
|
||||||
async create(obj, args) {
|
async create(obj, args, context) {
|
||||||
const group = await WIKI.db.pages.query().insertAndFetch({
|
const page = await WIKI.db.pages.query().insertAndFetch({
|
||||||
name: args.name
|
path: args.path,
|
||||||
|
title: args.title,
|
||||||
|
description: args.description,
|
||||||
|
isPrivate: false,
|
||||||
|
isPublished: args.isPublished,
|
||||||
|
publishStartDate: args.publishStartDate,
|
||||||
|
publishEndDate: args.publishEndDate,
|
||||||
|
localeCode: args.locale,
|
||||||
|
editorKey: args.editor,
|
||||||
|
authorId: context.req.user.id
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
responseResult: graphHelper.generateSuccess('Group created successfully.'),
|
responseResult: graphHelper.generateSuccess('Page created successfully.'),
|
||||||
group
|
page
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async delete(obj, args) {
|
async delete(obj, args) {
|
||||||
await WIKI.db.groups.query().deleteById(args.id)
|
await WIKI.db.groups.query().deleteById(args.id)
|
||||||
return {
|
return {
|
||||||
responseResult: graphHelper.generateSuccess('Group has been deleted.')
|
responseResult: graphHelper.generateSuccess('Page has been deleted.')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async update(obj, args) {
|
async update(obj, args) {
|
||||||
await WIKI.db.groups.query().patch({ name: args.name }).where('id', args.id)
|
await WIKI.db.groups.query().patch({ name: args.name }).where('id', args.id)
|
||||||
return {
|
return {
|
||||||
responseResult: graphHelper.generateSuccess('Group has been updated.')
|
responseResult: graphHelper.generateSuccess('Page has been updated.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -21,7 +21,10 @@ type PageQuery {
|
|||||||
): [PageMinimal]
|
): [PageMinimal]
|
||||||
|
|
||||||
single(
|
single(
|
||||||
id: Int!
|
id: Int
|
||||||
|
path: String
|
||||||
|
locale: String
|
||||||
|
isPrivate: Boolean
|
||||||
): Page
|
): Page
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,8 +35,10 @@ type PageQuery {
|
|||||||
type PageMutation {
|
type PageMutation {
|
||||||
create(
|
create(
|
||||||
description: String
|
description: String
|
||||||
isPublished: Boolean
|
editor: String
|
||||||
locale: String
|
isPublished: Boolean!
|
||||||
|
isPrivate: Boolean
|
||||||
|
locale: String!
|
||||||
path: String!
|
path: String!
|
||||||
publishEndDate: Date
|
publishEndDate: Date
|
||||||
publishStartDate: Date
|
publishStartDate: Date
|
||||||
@ -43,7 +48,15 @@ type PageMutation {
|
|||||||
|
|
||||||
update(
|
update(
|
||||||
id: Int!
|
id: Int!
|
||||||
name: String!
|
description: String
|
||||||
|
editor: String
|
||||||
|
isPublished: Boolean
|
||||||
|
locale: String
|
||||||
|
path: String
|
||||||
|
publishEndDate: Date
|
||||||
|
publishStartDate: Date
|
||||||
|
tags: [String]
|
||||||
|
title: String
|
||||||
): DefaultResponse
|
): DefaultResponse
|
||||||
|
|
||||||
delete(
|
delete(
|
||||||
|
@ -15,7 +15,7 @@ module.exports = {
|
|||||||
}, (uEmail, uPassword, done) => {
|
}, (uEmail, uPassword, done) => {
|
||||||
WIKI.db.users.query().findOne({
|
WIKI.db.users.query().findOne({
|
||||||
email: uEmail,
|
email: uEmail,
|
||||||
provider: 'local'
|
providerKey: 'local'
|
||||||
}).then((user) => {
|
}).then((user) => {
|
||||||
if (user) {
|
if (user) {
|
||||||
return user.verifyPassword(uPassword).then(() => {
|
return user.verifyPassword(uPassword).then(() => {
|
||||||
|
@ -325,7 +325,7 @@ module.exports = () => {
|
|||||||
// Create root administrator
|
// Create root administrator
|
||||||
WIKI.logger.info('Creating root administrator...')
|
WIKI.logger.info('Creating root administrator...')
|
||||||
await WIKI.db.users.query().delete().where({
|
await WIKI.db.users.query().delete().where({
|
||||||
provider: 'local',
|
providerKey: 'local',
|
||||||
email: req.body.adminEmail
|
email: req.body.adminEmail
|
||||||
})
|
})
|
||||||
await WIKI.db.users.query().insert({
|
await WIKI.db.users.query().insert({
|
||||||
@ -342,7 +342,7 @@ module.exports = () => {
|
|||||||
// Create Guest account
|
// Create Guest account
|
||||||
WIKI.logger.info('Creating guest account...')
|
WIKI.logger.info('Creating guest account...')
|
||||||
const guestUsr = await WIKI.db.users.query().findOne({
|
const guestUsr = await WIKI.db.users.query().findOne({
|
||||||
provider: 'local',
|
providerKey: 'local',
|
||||||
email: 'guest@example.com'
|
email: 'guest@example.com'
|
||||||
})
|
})
|
||||||
if (!guestUsr) {
|
if (!guestUsr) {
|
||||||
|
Loading…
Reference in New Issue
Block a user