Create mode + Source view + UI enhancements
This commit is contained in:
parent
0f06ab6dc8
commit
16f300f0d5
@ -19,8 +19,8 @@
|
|||||||
- [ ] Assets Management
|
- [ ] Assets Management
|
||||||
- [ ] Authentication
|
- [ ] Authentication
|
||||||
- [x] Caching
|
- [x] Caching
|
||||||
- [ ] Create Entry
|
- [x] Create Entry
|
||||||
- [ ] Edit Entry
|
- [x] Edit Entry
|
||||||
- [x] Git Management
|
- [x] Git Management
|
||||||
- [ ] History Management
|
- [ ] History Management
|
||||||
- [ ] Markdown Editor
|
- [ ] Markdown Editor
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
|||||||
"use strict";function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var _createClass=function(){function e(e,t){for(var n=0;n<t.length;n++){var a=t[n];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,a.key,a)}}return function(t,n,a){return n&&e(t.prototype,n),a&&e(t,a),t}}(),Alerts=function(){function e(){_classCallCheck(this,e);var t=this;t.mdl=new Vue({el:"#alerts",data:{children:[]},methods:{acknowledge:function(e){t.close(e)}}}),t.uidNext=1}return _createClass(e,[{key:"push",value:function(e){var t=this,n=_.defaults(e,{_uid:t.uidNext,class:"is-info",message:"---",sticky:!1,title:"---"});t.mdl.children.push(n),n.sticky||_.delay(function(){t.close(n._uid)},5e3),t.uidNext++}},{key:"pushError",value:function(e,t){this.push({class:"is-danger",message:t,sticky:!1,title:e})}},{key:"pushSuccess",value:function(e,t){this.push({class:"is-success",message:t,sticky:!1,title:e})}},{key:"close",value:function(e){var t=this,n=_.findIndex(t.mdl.children,["_uid",e]),a=_.nth(t.mdl.children,n);n>=0&&a&&(a.class+=" exit",t.mdl.children.$set(n,a),_.delay(function(){t.mdl.children.$remove(a)},500))}}]),e}();jQuery(document).ready(function(e){e("a").smoothScroll({speed:400,offset:-20});new Sticky(".stickyscroll");e(window).bind("beforeunload",function(){e("#notifload").addClass("active")}),e(document).ajaxSend(function(){e("#notifload").addClass("active")}).ajaxComplete(function(){e("#notifload").removeClass("active")});var t=new Alerts;if(alertsData&&_.forEach(alertsData,function(e){t.push(e)}),1===e("#mk-editor").length)var n=new SimpleMDE({autofocus:!0,autoDownloadFontAwesome:!1,element:e("#mk-editor").get(0),hideIcons:["heading","quote"],placeholder:"Enter Markdown formatted content here...",showIcons:["strikethrough","heading-1","heading-2","heading-3","code","table","horizontal-rule"],spellChecker:!1,status:!1});e("#page-type-edit").length&&(e(".btn-edit-discard").on("click",function(t){e("#modal-edit-discard").toggleClass("is-active")}),e(".btn-edit-save").on("click",function(a){e.ajax(window.location.href,{data:{markdown:n.value()},dataType:"json",method:"PUT"}).then(function(n,a,o){n.ok?window.location.assign("/"+e("#page-type-edit").data("entrypath")):t.pushError("Something went wrong",n.error)},function(e,n,a){t.pushError("Something went wrong","Save operation failed.")})}))});
|
"use strict";function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function setInputSelection(e,t,n){if(e.focus(),"undefined"!=typeof e.selectionStart)e.selectionStart=t,e.selectionEnd=n;else if(document.selection&&document.selection.createRange){e.select();var a=document.selection.createRange();a.collapse(!0),a.moveEnd("character",n),a.moveStart("character",t),a.select()}}function makeSafePath(e){var t=_.split(_.trim(e),"/");return t=_.map(t,function(e){return _.kebabCase(_.deburr(_.trim(e)))}),_.join(_.filter(t,function(e){return!_.isEmpty(e)}),"/")}var _createClass=function(){function e(e,t){for(var n=0;n<t.length;n++){var a=t[n];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,a.key,a)}}return function(t,n,a){return n&&e(t.prototype,n),a&&e(t,a),t}}(),Alerts=function(){function e(){_classCallCheck(this,e);var t=this;t.mdl=new Vue({el:"#alerts",data:{children:[]},methods:{acknowledge:function(e){t.close(e)}}}),t.uidNext=1}return _createClass(e,[{key:"push",value:function(e){var t=this,n=_.defaults(e,{_uid:t.uidNext,class:"is-info",message:"---",sticky:!1,title:"---"});t.mdl.children.push(n),n.sticky||_.delay(function(){t.close(n._uid)},5e3),t.uidNext++}},{key:"pushError",value:function(e,t){this.push({class:"is-danger",message:t,sticky:!1,title:e})}},{key:"pushSuccess",value:function(e,t){this.push({class:"is-success",message:t,sticky:!1,title:e})}},{key:"close",value:function(e){var t=this,n=_.findIndex(t.mdl.children,["_uid",e]),a=_.nth(t.mdl.children,n);n>=0&&a&&(a.class+=" exit",t.mdl.children.$set(n,a),_.delay(function(){t.mdl.children.$remove(a)},500))}}]),e}();jQuery(document).ready(function(e){e("a").smoothScroll({speed:400,offset:-20});new Sticky(".stickyscroll");e(window).bind("beforeunload",function(){e("#notifload").addClass("active")}),e(document).ajaxSend(function(){e("#notifload").addClass("active")}).ajaxComplete(function(){e("#notifload").removeClass("active")});var t=new Alerts;if(alertsData&&_.forEach(alertsData,function(e){t.push(e)}),1===e("#mk-editor").length)var n=new SimpleMDE({autofocus:!0,autoDownloadFontAwesome:!1,element:e("#mk-editor").get(0),hideIcons:["heading","quote"],placeholder:"Enter Markdown formatted content here...",showIcons:["strikethrough","heading-1","heading-2","heading-3","code","table","horizontal-rule"],spellChecker:!1,status:!1});if(e("#page-type-view").length&&!function(){var t="home"!==e("#page-type-view").data("entrypath")?e("#page-type-view").data("entrypath")+"/":"",n=t+"new-page";e(".btn-create-prompt").on("click",function(a){e("#txt-create-prompt").val(n),e("#modal-create-prompt").toggleClass("is-active"),setInputSelection(e("#txt-create-prompt").get(0),t.length,n.length),e("#txt-create-prompt").removeClass("is-danger").next().addClass("is-hidden")}),e("#txt-create-prompt").on("keypress",function(t){13===t.which&&e(".btn-create-go").trigger("click")}),e(".btn-create-go").on("click",function(t){var n=makeSafePath(e("#txt-create-prompt").val());_.isEmpty(n)?e("#txt-create-prompt").addClass("is-danger").next().removeClass("is-hidden"):(e("#txt-create-prompt").parent().addClass("is-loading"),window.location.assign("/create/"+n))})}(),e("#page-type-create").length&&(e(".btn-create-discard").on("click",function(t){e("#modal-create-discard").toggleClass("is-active")}),e(".btn-create-save").on("click",function(a){e.ajax(window.location.href,{data:{markdown:n.value()},dataType:"json",method:"PUT"}).then(function(n,a,o){n.ok?window.location.assign("/"+e("#page-type-create").data("entrypath")):t.pushError("Something went wrong",n.error)},function(e,n,a){t.pushError("Something went wrong","Save operation failed.")})})),e("#page-type-edit").length&&(e(".btn-edit-discard").on("click",function(t){e("#modal-edit-discard").toggleClass("is-active")}),e(".btn-edit-save").on("click",function(a){e.ajax(window.location.href,{data:{markdown:n.value()},dataType:"json",method:"PUT"}).then(function(n,a,o){n.ok?window.location.assign("/"+e("#page-type-edit").data("entrypath")):t.pushError("Something went wrong",n.error)},function(e,n,a){t.pushError("Something went wrong","Save operation failed.")})})),e("#page-type-source").length){var a=ace.edit("source-display");a.setTheme("ace/theme/tomorrow_night"),a.getSession().setMode("ace/mode/markdown"),a.setReadOnly(!0),a.renderer.updateFull(),console.log(a.getSession().getLength())}});
|
File diff suppressed because one or more lines are too long
4
client/content/create.md
Normal file
4
client/content/create.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<!-- TITLE: {TITLE} -->
|
||||||
|
<!-- SUBTITLE: A quick summary of {TITLE} -->
|
||||||
|
|
||||||
|
# Header
|
@ -56,6 +56,12 @@ jQuery( document ).ready(function( $ ) {
|
|||||||
// Pages logic
|
// Pages logic
|
||||||
// ====================================
|
// ====================================
|
||||||
|
|
||||||
|
//=include pages/view.js
|
||||||
|
//=include pages/create.js
|
||||||
//=include pages/edit.js
|
//=include pages/edit.js
|
||||||
|
//=include pages/source.js
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//=include helpers/form.js
|
||||||
|
//=include helpers/pages.js
|
16
client/js/helpers/form.js
Normal file
16
client/js/helpers/form.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
function setInputSelection(input, startPos, endPos) {
|
||||||
|
input.focus();
|
||||||
|
if (typeof input.selectionStart != "undefined") {
|
||||||
|
input.selectionStart = startPos;
|
||||||
|
input.selectionEnd = endPos;
|
||||||
|
} else if (document.selection && document.selection.createRange) {
|
||||||
|
// IE branch
|
||||||
|
input.select();
|
||||||
|
var range = document.selection.createRange();
|
||||||
|
range.collapse(true);
|
||||||
|
range.moveEnd("character", endPos);
|
||||||
|
range.moveStart("character", startPos);
|
||||||
|
range.select();
|
||||||
|
}
|
||||||
|
}
|
11
client/js/helpers/pages.js
Normal file
11
client/js/helpers/pages.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
function makeSafePath(rawPath) {
|
||||||
|
|
||||||
|
let rawParts = _.split(_.trim(rawPath), '/');
|
||||||
|
rawParts = _.map(rawParts, (r) => {
|
||||||
|
return _.kebabCase(_.deburr(_.trim(r)));
|
||||||
|
});
|
||||||
|
|
||||||
|
return _.join(_.filter(rawParts, (r) => { return !_.isEmpty(r); }), '/');
|
||||||
|
|
||||||
|
}
|
32
client/js/pages/create.js
Normal file
32
client/js/pages/create.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
if($('#page-type-create').length) {
|
||||||
|
|
||||||
|
//-> Discard
|
||||||
|
|
||||||
|
$('.btn-create-discard').on('click', (ev) => {
|
||||||
|
$('#modal-create-discard').toggleClass('is-active');
|
||||||
|
});
|
||||||
|
|
||||||
|
//-> Save
|
||||||
|
|
||||||
|
$('.btn-create-save').on('click', (ev) => {
|
||||||
|
|
||||||
|
$.ajax(window.location.href, {
|
||||||
|
data: {
|
||||||
|
markdown: mde.value()
|
||||||
|
},
|
||||||
|
dataType: 'json',
|
||||||
|
method: 'PUT'
|
||||||
|
}).then((rData, rStatus, rXHR) => {
|
||||||
|
if(rData.ok) {
|
||||||
|
window.location.assign('/' + $('#page-type-create').data('entrypath'));
|
||||||
|
} else {
|
||||||
|
alerts.pushError('Something went wrong', rData.error);
|
||||||
|
}
|
||||||
|
}, (rXHR, rStatus, err) => {
|
||||||
|
alerts.pushError('Something went wrong', 'Save operation failed.');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
12
client/js/pages/source.js
Normal file
12
client/js/pages/source.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
if($('#page-type-source').length) {
|
||||||
|
|
||||||
|
var scEditor = ace.edit("source-display");
|
||||||
|
scEditor.setTheme("ace/theme/tomorrow_night");
|
||||||
|
scEditor.getSession().setMode("ace/mode/markdown");
|
||||||
|
scEditor.setReadOnly(true);
|
||||||
|
scEditor.renderer.updateFull();
|
||||||
|
|
||||||
|
console.log(scEditor.getSession().getLength());
|
||||||
|
|
||||||
|
}
|
34
client/js/pages/view.js
Normal file
34
client/js/pages/view.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
|
||||||
|
if($('#page-type-view').length) {
|
||||||
|
|
||||||
|
let currentBasePath = ($('#page-type-view').data('entrypath') !== 'home') ? $('#page-type-view').data('entrypath') + '/' : '';
|
||||||
|
let suggestedCreatePath = currentBasePath + 'new-page';
|
||||||
|
|
||||||
|
//-> Create New Document
|
||||||
|
|
||||||
|
$('.btn-create-prompt').on('click', (ev) => {
|
||||||
|
$('#txt-create-prompt').val(suggestedCreatePath);
|
||||||
|
$('#modal-create-prompt').toggleClass('is-active');
|
||||||
|
setInputSelection($('#txt-create-prompt').get(0), currentBasePath.length, suggestedCreatePath.length);
|
||||||
|
$('#txt-create-prompt').removeClass('is-danger').next().addClass('is-hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#txt-create-prompt').on('keypress', (ev) => {
|
||||||
|
if(ev.which === 13) {
|
||||||
|
$('.btn-create-go').trigger('click');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.btn-create-go').on('click', (ev) => {
|
||||||
|
|
||||||
|
let newDocPath = makeSafePath($('#txt-create-prompt').val());
|
||||||
|
if(_.isEmpty(newDocPath)) {
|
||||||
|
$('#txt-create-prompt').addClass('is-danger').next().removeClass('is-hidden');
|
||||||
|
} else {
|
||||||
|
$('#txt-create-prompt').parent().addClass('is-loading');
|
||||||
|
window.location.assign('/create/' + newDocPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
@ -5,4 +5,17 @@
|
|||||||
|
|
||||||
.editor-toolbar .fa {
|
.editor-toolbar .fa {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ace-container {
|
||||||
|
position: relative;
|
||||||
|
min-height: 95vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#source-display {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
}
|
}
|
@ -16,7 +16,7 @@
|
|||||||
border-bottom: 1px dotted $grey-light;
|
border-bottom: 1px dotted $grey-light;
|
||||||
padding-bottom: 4px;
|
padding-bottom: 4px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
color: $grey-dark;
|
color: desaturate(darken($purple, 15%), 10%);
|
||||||
}
|
}
|
||||||
|
|
||||||
a.toc-anchor {
|
a.toc-anchor {
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
|
|
||||||
|
#header {
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
h2.nav-item {
|
h2.nav-item {
|
||||||
font-size: 150%;
|
font-size: 150%;
|
||||||
color: $orange;
|
color: $orange;
|
||||||
|
@ -58,14 +58,95 @@ router.put('/edit/*', (req, res, next) => {
|
|||||||
// CREATE MODE
|
// CREATE MODE
|
||||||
// ==========================================
|
// ==========================================
|
||||||
|
|
||||||
router.get('/new/*', (req, res, next) => {
|
router.get('/create/*', (req, res, next) => {
|
||||||
res.send('CREATE MODE');
|
|
||||||
|
if(_.some(['create','edit','account','source','history','mk'], (e) => { return _.startsWith(req.path, '/create/' + e); })) {
|
||||||
|
return res.render('error', {
|
||||||
|
message: 'You cannot create a document with this name as it is reserved by the system.',
|
||||||
|
error: {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let safePath = entries.parsePath(_.replace(req.path, '/create', ''));
|
||||||
|
|
||||||
|
entries.exists(safePath).then((docExists) => {
|
||||||
|
if(!docExists) {
|
||||||
|
entries.getStarter(safePath).then((contents) => {
|
||||||
|
|
||||||
|
let pageData = {
|
||||||
|
markdown: contents,
|
||||||
|
meta: {
|
||||||
|
title: _.startCase(safePath),
|
||||||
|
path: safePath
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return res.render('pages/create', { pageData });
|
||||||
|
|
||||||
|
}).catch((err) => {
|
||||||
|
throw new Error('Could not load starter content!');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new Error('This entry already exists!');
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
res.render('error', {
|
||||||
|
message: err.message,
|
||||||
|
error: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
router.put('/create/*', (req, res, next) => {
|
||||||
|
|
||||||
|
let safePath = entries.parsePath(_.replace(req.path, '/create', ''));
|
||||||
|
|
||||||
|
entries.create(safePath, req.body.markdown).then(() => {
|
||||||
|
res.json({
|
||||||
|
ok: true
|
||||||
|
});
|
||||||
|
}).catch((err) => {
|
||||||
|
res.json({
|
||||||
|
ok: false,
|
||||||
|
error: err.message
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// VIEW MODE
|
// VIEW MODE
|
||||||
// ==========================================
|
// ==========================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View source of a document
|
||||||
|
*/
|
||||||
|
router.get('/source/*', (req, res, next) => {
|
||||||
|
|
||||||
|
let safePath = entries.parsePath(_.replace(req.path, '/source', ''));
|
||||||
|
|
||||||
|
entries.fetchOriginal(safePath, {
|
||||||
|
parseMarkdown: false,
|
||||||
|
parseMeta: true,
|
||||||
|
parseTree: false,
|
||||||
|
includeMarkdown: true,
|
||||||
|
includeParentInfo: false,
|
||||||
|
cache: false
|
||||||
|
}).then((pageData) => {
|
||||||
|
if(pageData) {
|
||||||
|
return res.render('pages/source', { pageData });
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid page path.');
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
res.render('error', {
|
||||||
|
message: err.message,
|
||||||
|
error: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View document
|
* View document
|
||||||
*/
|
*/
|
||||||
@ -77,11 +158,16 @@ router.get('/*', (req, res, next) => {
|
|||||||
if(pageData) {
|
if(pageData) {
|
||||||
return res.render('pages/view', { pageData });
|
return res.render('pages/view', { pageData });
|
||||||
} else {
|
} else {
|
||||||
return next();
|
res.render('error', {
|
||||||
|
message: err.message,
|
||||||
|
error: {}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
winston.error(err);
|
res.render('error', {
|
||||||
next();
|
message: err.message,
|
||||||
|
error: {}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
15
gulpfile.js
15
gulpfile.js
@ -19,12 +19,15 @@ var include = require("gulp-include");
|
|||||||
*/
|
*/
|
||||||
var paths = {
|
var paths = {
|
||||||
scriptlibs: [
|
scriptlibs: [
|
||||||
'./node_modules/lodash/lodash.min.js',
|
'./node_modules/jquery/dist/jquery.min.js',
|
||||||
'./node_modules/jquery/dist/jquery.min.js',
|
'./node_modules/vue/dist/vue.min.js',
|
||||||
'./node_modules/vue/dist/vue.min.js',
|
'./node_modules/jquery-smooth-scroll/jquery.smooth-scroll.min.js',
|
||||||
'./node_modules/jquery-smooth-scroll/jquery.smooth-scroll.min.js',
|
'./node_modules/sticky-js/dist/sticky.min.js',
|
||||||
'./node_modules/sticky-js/dist/sticky.min.js',
|
'./node_modules/simplemde/dist/simplemde.min.js',
|
||||||
'./node_modules/simplemde/dist/simplemde.min.js'
|
'./node_modules/ace-builds/src-min-noconflict/ace.js',
|
||||||
|
'./node_modules/ace-builds/src-min-noconflict/mode-markdown.js',
|
||||||
|
'./node_modules/ace-builds/src-min-noconflict/theme-tomorrow_night.js',
|
||||||
|
'./node_modules/lodash/lodash.min.js'
|
||||||
],
|
],
|
||||||
scriptapps: [
|
scriptapps: [
|
||||||
'./client/js/components/*.js',
|
'./client/js/components/*.js',
|
||||||
|
@ -33,6 +33,31 @@ module.exports = {
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a document already exists
|
||||||
|
*
|
||||||
|
* @param {String} entryPath The entry path
|
||||||
|
* @return {Promise<Boolean>} True if exists, false otherwise
|
||||||
|
*/
|
||||||
|
exists(entryPath) {
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
return self.fetchOriginal(entryPath, {
|
||||||
|
parseMarkdown: false,
|
||||||
|
parseMeta: false,
|
||||||
|
parseTree: false,
|
||||||
|
includeMarkdown: false,
|
||||||
|
includeParentInfo: false,
|
||||||
|
cache: false
|
||||||
|
}).then(() => {
|
||||||
|
return true;
|
||||||
|
}).catch((err) => {
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch a document from cache, otherwise the original
|
* Fetch a document from cache, otherwise the original
|
||||||
*
|
*
|
||||||
@ -145,6 +170,8 @@ module.exports = {
|
|||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
return Promise.reject(new Error('Entry ' + entryPath + ' does not exist!'));
|
||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
@ -253,7 +280,33 @@ module.exports = {
|
|||||||
return Promise.reject(new Error('Entry does not exist!'));
|
return Promise.reject(new Error('Entry does not exist!'));
|
||||||
}
|
}
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
return new Error('Entry does not exist!');
|
return Promise.reject(new Error('Entry does not exist!'));
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new document
|
||||||
|
*
|
||||||
|
* @param {String} entryPath The entry path
|
||||||
|
* @param {String} contents The markdown-formatted contents
|
||||||
|
* @return {Promise<Boolean>} True on success, false on failure
|
||||||
|
*/
|
||||||
|
create(entryPath, contents) {
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
return self.exists(entryPath).then((docExists) => {
|
||||||
|
if(!docExists) {
|
||||||
|
return self.makePersistent(entryPath, contents).then(() => {
|
||||||
|
return self.fetchOriginal(entryPath, {});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return Promise.reject(new Error('Entry already exists!'));
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
winston.error(err);
|
||||||
|
return Promise.reject(new Error('Something went wrong.'));
|
||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
@ -274,6 +327,23 @@ module.exports = {
|
|||||||
return git.commitDocument(entryPath);
|
return git.commitDocument(entryPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a starter page content based on the entry path
|
||||||
|
*
|
||||||
|
* @param {String} entryPath The entry path
|
||||||
|
* @return {Promise<String>} Starter content
|
||||||
|
*/
|
||||||
|
getStarter(entryPath) {
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
let formattedTitle = _.startCase(_.last(_.split(entryPath, '/')));
|
||||||
|
|
||||||
|
return fs.readFileAsync(path.join(ROOTPATH, 'client/content/create.md'), 'utf8').then((contents) => {
|
||||||
|
return _.replace(contents, new RegExp('{TITLE}', 'g'), formattedTitle);
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
@ -11,7 +11,6 @@ var Promise = require('bluebird'),
|
|||||||
mdExpandTabs = require('markdown-it-expand-tabs'),
|
mdExpandTabs = require('markdown-it-expand-tabs'),
|
||||||
mdAttrs = require('markdown-it-attrs'),
|
mdAttrs = require('markdown-it-attrs'),
|
||||||
hljs = require('highlight.js'),
|
hljs = require('highlight.js'),
|
||||||
slug = require('slug'),
|
|
||||||
cheerio = require('cheerio'),
|
cheerio = require('cheerio'),
|
||||||
_ = require('lodash');
|
_ = require('lodash');
|
||||||
|
|
||||||
@ -81,10 +80,10 @@ const parseTree = (content) => {
|
|||||||
let anchor = "";
|
let anchor = "";
|
||||||
if (heading.children && heading.children[0].type === "link_open") {
|
if (heading.children && heading.children[0].type === "link_open") {
|
||||||
content = heading.children[1].content;
|
content = heading.children[1].content;
|
||||||
anchor = slug(content, {lower: true});
|
anchor = _.kebabCase(content);
|
||||||
} else {
|
} else {
|
||||||
content = heading.content
|
content = heading.content
|
||||||
anchor = slug(heading.children.reduce((acc, t) => acc + t.content, ""), {lower: true});
|
anchor = _.kebabCase(heading.children.reduce((acc, t) => acc + t.content, ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
tocArray.push({
|
tocArray.push({
|
||||||
|
10
package.json
10
package.json
@ -35,7 +35,6 @@
|
|||||||
"bluebird": "^3.4.1",
|
"bluebird": "^3.4.1",
|
||||||
"body-parser": "^1.15.2",
|
"body-parser": "^1.15.2",
|
||||||
"bson": "^0.5.4",
|
"bson": "^0.5.4",
|
||||||
"bulma": "^0.1.2",
|
|
||||||
"cheerio": "^0.22.0",
|
"cheerio": "^0.22.0",
|
||||||
"child-process-promise": "^2.1.3",
|
"child-process-promise": "^2.1.3",
|
||||||
"compression": "^1.6.2",
|
"compression": "^1.6.2",
|
||||||
@ -55,7 +54,6 @@
|
|||||||
"i18next": "^3.4.1",
|
"i18next": "^3.4.1",
|
||||||
"i18next-express-middleware": "^1.0.1",
|
"i18next-express-middleware": "^1.0.1",
|
||||||
"i18next-node-fs-backend": "^0.1.2",
|
"i18next-node-fs-backend": "^0.1.2",
|
||||||
"jquery-smooth-scroll": "^2.0.0",
|
|
||||||
"js-yaml": "^3.6.1",
|
"js-yaml": "^3.6.1",
|
||||||
"lodash": "^4.15.0",
|
"lodash": "^4.15.0",
|
||||||
"lokijs": "^1.4.1",
|
"lokijs": "^1.4.1",
|
||||||
@ -77,15 +75,14 @@
|
|||||||
"search-index": "^0.8.15",
|
"search-index": "^0.8.15",
|
||||||
"serve-favicon": "^2.3.0",
|
"serve-favicon": "^2.3.0",
|
||||||
"simplemde": "^1.11.2",
|
"simplemde": "^1.11.2",
|
||||||
"slug": "^0.9.1",
|
|
||||||
"sticky-js": "^1.0.5",
|
|
||||||
"twemoji-awesome": "^1.0.4",
|
|
||||||
"validator": "^5.5.0",
|
"validator": "^5.5.0",
|
||||||
"validator-as-promised": "^1.0.2",
|
"validator-as-promised": "^1.0.2",
|
||||||
"winston": "^2.2.0"
|
"winston": "^2.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"ace-builds": "^1.2.5",
|
||||||
"babel-preset-es2015": "^6.13.2",
|
"babel-preset-es2015": "^6.13.2",
|
||||||
|
"bulma": "^0.1.2",
|
||||||
"chai": "^3.5.0",
|
"chai": "^3.5.0",
|
||||||
"chai-as-promised": "^5.3.0",
|
"chai-as-promised": "^5.3.0",
|
||||||
"codacy-coverage": "^2.0.0",
|
"codacy-coverage": "^2.0.0",
|
||||||
@ -104,11 +101,14 @@
|
|||||||
"gulp-zip": "^3.2.0",
|
"gulp-zip": "^3.2.0",
|
||||||
"istanbul": "^0.4.5",
|
"istanbul": "^0.4.5",
|
||||||
"jquery": "^3.1.0",
|
"jquery": "^3.1.0",
|
||||||
|
"jquery-smooth-scroll": "^2.0.0",
|
||||||
"merge-stream": "^1.0.0",
|
"merge-stream": "^1.0.0",
|
||||||
"mocha": "^3.0.2",
|
"mocha": "^3.0.2",
|
||||||
"mocha-lcov-reporter": "^1.2.0",
|
"mocha-lcov-reporter": "^1.2.0",
|
||||||
"nodemon": "^1.10.2",
|
"nodemon": "^1.10.2",
|
||||||
|
"twemoji-awesome": "^1.0.4",
|
||||||
"snyk": "^1.18.1",
|
"snyk": "^1.18.1",
|
||||||
|
"sticky-js": "^1.0.5",
|
||||||
"vue": "^1.0.26"
|
"vue": "^1.0.26"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ footer.footer
|
|||||||
p
|
p
|
||||||
= t('footer.poweredby') + ' '
|
= t('footer.poweredby') + ' '
|
||||||
a(href='https://github.com/Requarks/wiki') Requarks Wiki
|
a(href='https://github.com/Requarks/wiki') Requarks Wiki
|
||||||
| .
|
p
|
||||||
p
|
a.icon(href='https://github.com/Requarks/wiki')
|
||||||
a.icon(href='https://github.com/Requarks/wiki')
|
i.fa.fa-github
|
||||||
i.fa.fa-github
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
nav.nav.has-shadow.stickyscroll
|
nav.nav.has-shadow.stickyscroll#header
|
||||||
.nav-left
|
.nav-left
|
||||||
block rootNavLeft
|
block rootNavLeft
|
||||||
a.nav-item.is-brand(href='/')
|
a.nav-item.is-brand(href='/')
|
||||||
@ -17,16 +17,16 @@ nav.nav.has-shadow.stickyscroll
|
|||||||
.nav-right.nav-menu
|
.nav-right.nav-menu
|
||||||
block rootNavRight
|
block rootNavRight
|
||||||
i.nav-item#notifload
|
i.nav-item#notifload
|
||||||
a.nav-item(href='#')
|
a.nav-item(href='/history/' + pageData.meta.path)
|
||||||
| History
|
| History
|
||||||
a.nav-item(href='#')
|
a.nav-item(href='/source/' + pageData.meta.path)
|
||||||
| Source
|
| Source
|
||||||
span.nav-item
|
span.nav-item
|
||||||
a.button
|
a.button(href='/edit/' + pageData.meta.path)
|
||||||
span.icon
|
span.icon
|
||||||
i.fa.fa-edit
|
i.fa.fa-edit
|
||||||
span Edit
|
span Edit
|
||||||
a.button.is-primary(href='#', onclick='$(".modal").addClass("is-active");')
|
a.button.is-primary.btn-create-prompt
|
||||||
span.icon
|
span.icon
|
||||||
i.fa.fa-plus
|
i.fa.fa-plus
|
||||||
span Create
|
span Create
|
||||||
|
17
views/modals/create.pug
Normal file
17
views/modals/create.pug
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
.modal#modal-create-prompt
|
||||||
|
.modal-background
|
||||||
|
.modal-container
|
||||||
|
.modal-content
|
||||||
|
.card.is-fullwidth
|
||||||
|
header.card-header
|
||||||
|
p.card-header-title Create New Page
|
||||||
|
.card-content
|
||||||
|
.content
|
||||||
|
label.label Enter the new document path:
|
||||||
|
p.control
|
||||||
|
input.input(type='text', placeholder='page-name')#txt-create-prompt
|
||||||
|
span.help.is-danger.is-hidden This document path is invalid!
|
||||||
|
footer.card-footer
|
||||||
|
a.card-footer-item.btn-create-prompt Discard
|
||||||
|
a.card-footer-item.btn-create-go Create
|
14
views/modals/edit.pug
Normal file
14
views/modals/edit.pug
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
.modal#modal-edit-discard
|
||||||
|
.modal-background
|
||||||
|
.modal-container
|
||||||
|
.modal-content
|
||||||
|
.card.is-fullwidth
|
||||||
|
header.card-header.is-warning
|
||||||
|
p.card-header-title Discard?
|
||||||
|
.card-content
|
||||||
|
.content
|
||||||
|
| Are you sure you want to leave this page and loose any modifications?
|
||||||
|
footer.card-footer
|
||||||
|
a.card-footer-item.btn-edit-discard Stay on page
|
||||||
|
a.card-footer-item(href='/' + pageData.meta.path) Discard
|
36
views/pages/create.pug
Normal file
36
views/pages/create.pug
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
extends ../layout
|
||||||
|
|
||||||
|
block rootNavCenter
|
||||||
|
h2.nav-item Create New Document
|
||||||
|
|
||||||
|
block rootNavRight
|
||||||
|
i.nav-item#notifload
|
||||||
|
span.nav-item
|
||||||
|
a.button.is-warning.btn-create-discard
|
||||||
|
span.icon
|
||||||
|
i.fa.fa-times
|
||||||
|
span Discard
|
||||||
|
a.button.is-success.btn-create-save
|
||||||
|
span.icon
|
||||||
|
i.fa.fa-check
|
||||||
|
span Save Document
|
||||||
|
|
||||||
|
block content
|
||||||
|
|
||||||
|
#page-type-create(data-entrypath=pageData.meta.path)
|
||||||
|
section.section.is-small
|
||||||
|
textarea#mk-editor= pageData.markdown
|
||||||
|
|
||||||
|
.modal#modal-create-discard
|
||||||
|
.modal-background
|
||||||
|
.modal-container
|
||||||
|
.modal-content
|
||||||
|
.card.is-fullwidth
|
||||||
|
header.card-header.is-warning
|
||||||
|
p.card-header-title Discard?
|
||||||
|
.card-content
|
||||||
|
.content
|
||||||
|
| Are you sure you want to leave this page and loose anything you wrote so far?
|
||||||
|
footer.card-footer
|
||||||
|
a.card-footer-item.btn-create-discard Stay on page
|
||||||
|
a.card-footer-item(href='/') Discard
|
@ -5,9 +5,9 @@ block rootNavCenter
|
|||||||
|
|
||||||
block rootNavRight
|
block rootNavRight
|
||||||
i.nav-item#notifload
|
i.nav-item#notifload
|
||||||
a.nav-item(href='#')
|
a.nav-item(href='/history/' + pageData.meta.path, target='_blank')
|
||||||
| History
|
| History
|
||||||
a.nav-item(href='#')
|
a.nav-item(href='/source/' + pageData.meta.path, target='_blank')
|
||||||
| Source
|
| Source
|
||||||
span.nav-item
|
span.nav-item
|
||||||
a.button.is-warning.btn-edit-discard
|
a.button.is-warning.btn-edit-discard
|
||||||
@ -25,16 +25,4 @@ block content
|
|||||||
section.section.is-small
|
section.section.is-small
|
||||||
textarea#mk-editor= pageData.markdown
|
textarea#mk-editor= pageData.markdown
|
||||||
|
|
||||||
.modal#modal-edit-discard
|
include ../modals/edit
|
||||||
.modal-background
|
|
||||||
.modal-container
|
|
||||||
.modal-content
|
|
||||||
.card.is-fullwidth
|
|
||||||
header.card-header.is-warning
|
|
||||||
p.card-header-title Discard?
|
|
||||||
.card-content
|
|
||||||
.content
|
|
||||||
| Are you sure you want to leave this page and loose any modifications?
|
|
||||||
footer.card-footer
|
|
||||||
a.card-footer-item.btn-edit-discard Stay on page
|
|
||||||
a.card-footer-item(href='/' + pageData.meta.path) Discard
|
|
9
views/pages/source.pug
Normal file
9
views/pages/source.pug
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
extends ../layout
|
||||||
|
|
||||||
|
block content
|
||||||
|
|
||||||
|
#page-type-source(data-entrypath=pageData.meta.path)
|
||||||
|
.ace-container
|
||||||
|
#source-display= pageData.markdown
|
||||||
|
|
||||||
|
include ../modals/create
|
@ -10,16 +10,16 @@ mixin tocMenu(ti)
|
|||||||
|
|
||||||
block rootNavRight
|
block rootNavRight
|
||||||
i.nav-item#notifload
|
i.nav-item#notifload
|
||||||
a.nav-item(href='#')
|
a.nav-item(href='/history/' + pageData.meta.path)
|
||||||
| History
|
| History
|
||||||
a.nav-item(href='#')
|
a.nav-item(href='/source/' + pageData.meta.path)
|
||||||
| Source
|
| Source
|
||||||
span.nav-item
|
span.nav-item
|
||||||
a.button(href='/edit/' + pageData.meta.path)
|
a.button(href='/edit/' + pageData.meta.path)
|
||||||
span.icon
|
span.icon
|
||||||
i.fa.fa-edit
|
i.fa.fa-edit
|
||||||
span Edit
|
span Edit
|
||||||
a.button.is-primary(href='#', onclick='$(".modal").addClass("is-active");')
|
a.button.is-primary.btn-create-prompt
|
||||||
span.icon
|
span.icon
|
||||||
i.fa.fa-plus
|
i.fa.fa-plus
|
||||||
span Create
|
span Create
|
||||||
@ -61,18 +61,4 @@ block content
|
|||||||
.content.mkcontent
|
.content.mkcontent
|
||||||
!= pageData.html
|
!= pageData.html
|
||||||
|
|
||||||
.modal
|
include ../modals/create
|
||||||
.modal-background
|
|
||||||
.modal-container
|
|
||||||
.modal-content
|
|
||||||
.card.is-fullwidth
|
|
||||||
header.card-header
|
|
||||||
p.card-header-title Create New Page
|
|
||||||
.card-content
|
|
||||||
.content
|
|
||||||
label.label Enter the new document name:
|
|
||||||
p.control
|
|
||||||
input.input(type='text', placeholder='page-name')
|
|
||||||
footer.card-footer
|
|
||||||
a.card-footer-item(onclick='$(".modal").removeClass("is-active");') Discard
|
|
||||||
a.card-footer-item.featured Create
|
|
||||||
|
Loading…
Reference in New Issue
Block a user