From 516220bddaee6723ad8ff81084114d8990aaaa2b Mon Sep 17 00:00:00 2001 From: NGPixel Date: Mon, 9 Oct 2017 21:43:43 -0400 Subject: [PATCH] feat: delete a page --- client/js/app.js | 2 + client/js/components/modal-delete-page.vue | 66 ++++++++++++++++++++ client/js/store/index.js | 2 + client/js/store/modules/modal-delete-page.js | 14 +++++ client/scss/components/nav.scss | 6 +- server/controllers/pages.js | 25 ++++++++ server/libs/entries.js | 26 ++++++++ server/libs/git.js | 23 +++++++ server/locales/en/browser.json | 4 +- server/views/pages/view.pug | 3 + yarn.lock | 2 +- 11 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 client/js/components/modal-delete-page.vue create mode 100644 client/js/store/modules/modal-delete-page.js diff --git a/client/js/app.js b/client/js/app.js index 7c4b5899..9a9c3b1b 100644 --- a/client/js/app.js +++ b/client/js/app.js @@ -38,6 +38,7 @@ import historyComponent from './components/history.vue' import loadingSpinnerComponent from './components/loading-spinner.vue' import modalCreatePageComponent from './components/modal-create-page.vue' import modalCreateUserComponent from './components/modal-create-user.vue' +import modalDeletePageComponent from './components/modal-delete-page.vue' import modalDeleteUserComponent from './components/modal-delete-user.vue' import modalDiscardPageComponent from './components/modal-discard-page.vue' import modalMovePageComponent from './components/modal-move-page.vue' @@ -86,6 +87,7 @@ Vue.component('history', historyComponent) Vue.component('loadingSpinner', loadingSpinnerComponent) Vue.component('modalCreatePage', modalCreatePageComponent) Vue.component('modalCreateUser', modalCreateUserComponent) +Vue.component('modalDeletePage', modalDeletePageComponent) Vue.component('modalDeleteUser', modalDeleteUserComponent) Vue.component('modalDiscardPage', modalDiscardPageComponent) Vue.component('modalMovePage', modalMovePageComponent) diff --git a/client/js/components/modal-delete-page.vue b/client/js/components/modal-delete-page.vue new file mode 100644 index 00000000..e188e794 --- /dev/null +++ b/client/js/components/modal-delete-page.vue @@ -0,0 +1,66 @@ + + + diff --git a/client/js/store/index.js b/client/js/store/index.js index cf151045..471c9d87 100644 --- a/client/js/store/index.js +++ b/client/js/store/index.js @@ -10,6 +10,7 @@ import editorVideo from './modules/editor-video' import modalCreatePage from './modules/modal-create-page' import modalCreateUser from './modules/modal-create-user' import modalDeleteUser from './modules/modal-delete-user' +import modalDeletePage from './modules/modal-delete-page' import modalDiscardPage from './modules/modal-discard-page' import modalMovePage from './modules/modal-move-page' import modalProfile2fa from './modules/modal-profile-2fa' @@ -39,6 +40,7 @@ export default new Vuex.Store({ editorVideo, modalCreatePage, modalCreateUser, + modalDeletePage, modalDeleteUser, modalDiscardPage, modalMovePage, diff --git a/client/js/store/modules/modal-delete-page.js b/client/js/store/modules/modal-delete-page.js new file mode 100644 index 00000000..7dc8d763 --- /dev/null +++ b/client/js/store/modules/modal-delete-page.js @@ -0,0 +1,14 @@ +export default { + namespaced: true, + state: { + shown: false + }, + getters: {}, + mutations: { + shownChange: (state, shownState) => { state.shown = shownState } + }, + actions: { + open({ commit }) { commit('shownChange', true) }, + close({ commit }) { commit('shownChange', false) } + } +} diff --git a/client/scss/components/nav.scss b/client/scss/components/nav.scss index c5a76ff3..0590ec37 100644 --- a/client/scss/components/nav.scss +++ b/client/scss/components/nav.scss @@ -173,7 +173,11 @@ &.is-outlined { background-color: mc($primary, '500'); color: mc($primary, '100'); - } + } + + &.is-icon-only i { + margin-right: 0; + } &:hover { background-color: mc($primary, '700'); diff --git a/server/controllers/pages.js b/server/controllers/pages.js index 951294e3..c974112c 100644 --- a/server/controllers/pages.js +++ b/server/controllers/pages.js @@ -288,4 +288,29 @@ router.put('/*', (req, res, next) => { }) }) +/** + * Delete document + */ +router.delete('/*', (req, res, next) => { + if (!res.locals.rights.write) { + return res.json({ + ok: false, + error: lang.t('errors:forbidden') + }) + } + + let safePath = entryHelper.parsePath(req.path) + + entries.remove(safePath, req.user).then(() => { + res.json({ + ok: true + }) + }).catch((err) => { + res.json({ + ok: false, + error: err.message + }) + }) +}) + module.exports = router diff --git a/server/libs/entries.js b/server/libs/entries.js index a5e7b830..af6f1104 100644 --- a/server/libs/entries.js +++ b/server/libs/entries.js @@ -388,6 +388,32 @@ module.exports = { }) }, + /** + * Delete a document + * + * @param {String} entryPath The current entry path + * @param {Object} author The author user object + * @return {Promise} Promise of the operation + */ + remove(entryPath, author) { + if (_.isEmpty(entryPath) || entryPath === 'home') { + return Promise.reject(new Error(lang.t('errors:invalidpath'))) + } + + return git.deleteDocument(entryPath, author).then(() => { + // Delete old cache version + + let oldEntryCachePath = entryHelper.getCachePath(entryPath) + fs.unlinkAsync(oldEntryCachePath).catch((err) => { return true }) // eslint-disable-line handle-callback-err + + // Delete old index entry + search.delete(entryPath) + + // Delete entry + return db.Entry.deleteOne({ _id: entryPath }) + }) + }, + /** * Generate a starter page content based on the entry path * diff --git a/server/libs/git.js b/server/libs/git.js index 805acb55..7787f37a 100644 --- a/server/libs/git.js +++ b/server/libs/git.js @@ -245,6 +245,29 @@ module.exports = { }) }, + /** + * Delete a document. + * + * @param {String} entryPath The entry path + * @return {Promise} Resolve on success + */ + deleteDocument(entryPath, author) { + let self = this + let gitFilePath = entryPath + '.md' + + return this._git.exec('rm', [gitFilePath]).then((cProc) => { + let out = cProc.stdout.toString() + if (_.includes(out, 'fatal')) { + let errorMsg = _.capitalize(_.head(_.split(_.replace(out, 'fatal: ', ''), ','))) + throw new Error(errorMsg) + } + let commitUsr = securityHelper.sanitizeCommitUser(author) + return self._git.exec('commit', ['-m', lang.t('git:deleted', { path: gitFilePath }), '--author="' + commitUsr.name + ' <' + commitUsr.email + '>"']).catch((err) => { + if (_.includes(err.stdout, 'nothing to commit')) { return true } + }) + }) + }, + /** * Commits uploads changes. * diff --git a/server/locales/en/browser.json b/server/locales/en/browser.json index 9d4e4f93..651fbd5f 100644 --- a/server/locales/en/browser.json +++ b/server/locales/en/browser.json @@ -77,6 +77,8 @@ "delete": "Delete", "deletefiletitle": "Delete?", "deletefilewarn": "Are you sure you want to delete", + "deletepagewarning": "Are you sure you want to delete this page? This action cannot be undone!", + "deletepagetitle": "Delete this page?", "deleteusertitle": "Delete User Account?", "deleteuserwarning": "Are you sure you want to delete this user account? This action cannot be undone!", "discard": "Discard", @@ -113,4 +115,4 @@ "placeholder": "Search...", "results": "Search Results" } -} \ No newline at end of file +} diff --git a/server/views/pages/view.pug b/server/views/pages/view.pug index 773f0b66..76462c38 100644 --- a/server/views/pages/view.pug +++ b/server/views/pages/view.pug @@ -12,6 +12,8 @@ block rootNavRight loading-spinner .nav-item if rights.write && pageData.meta.path !== 'home' + a.button.is-outlined.is-icon-only(@click='$store.dispatch("modalDeletePage/open")') + i.nc-icon-outline.ui-1_trash a.button.is-outlined(v-on:click='$store.dispatch("modalMovePage/open")') i.nc-icon-outline.arrows-1_shuffle-98 span= t('nav.move') @@ -83,4 +85,5 @@ block content modal-create-page(basepath=pageData.meta.path) modal-move-page(current-path=pageData.meta.path) + modal-delete-page(current-path=pageData.meta.path) anchor diff --git a/yarn.lock b/yarn.lock index 5c7a6749..65a2b572 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5137,7 +5137,7 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" -opencollective@^1.0.3: +opencollective@^1.0.3, opencollective@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/opencollective/-/opencollective-1.0.3.tgz#aee6372bc28144583690c3ca8daecfc120dd0ef1" dependencies: