mirror of
https://github.com/codex-team/codex.docs.git
synced 2025-07-19 05:09:41 +02:00
Editorjs checklist tool (#98)
Co-authored-by: Peter Savchenko <specc.dev@gmail.com>
This commit is contained in:
parent
c0a4f6f3fd
commit
b744ed592a
30 changed files with 2628 additions and 2152 deletions
16
package.json
16
package.json
|
@ -13,7 +13,7 @@
|
|||
"build:dev": "webpack ./src/frontend/js/app.js --o='./public/dist/[name].bundle.js' --output-library=Docs --output-public-path=/dist/ -p --mode=development --watch",
|
||||
"precommit": "yarn lint && yarn test --exit",
|
||||
"generatePassword": "node ./generatePassword.js",
|
||||
"editor-upgrade": "yarn add -D @editorjs/{editorjs,header,code,delimiter,list,image,table,inline-code,marker,warning}@latest"
|
||||
"editor-upgrade": "yarn add -D @editorjs/{editorjs,header,code,delimiter,list,image,table,inline-code,marker,warning,checklist}@latest"
|
||||
},
|
||||
"dependencies": {
|
||||
"bcrypt": "^3.0.3",
|
||||
|
@ -35,6 +35,7 @@
|
|||
"node-fetch": "^2.3.0",
|
||||
"nodemon": "^1.18.3",
|
||||
"twig": "~1.12.0",
|
||||
"typescript-eslint": "^0.0.1-alpha.0",
|
||||
"uuid4": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -43,15 +44,16 @@
|
|||
"@babel/polyfill": "^7.2.5",
|
||||
"@babel/preset-env": "^7.1.0",
|
||||
"@codexteam/misprints": "^1.0.0",
|
||||
"@editorjs/checklist": "^1.1.0",
|
||||
"@editorjs/code": "^2.4.1",
|
||||
"@editorjs/delimiter": "^1.1.0",
|
||||
"@editorjs/editorjs": "^2.16.0",
|
||||
"@editorjs/header": "^2.3.2",
|
||||
"@editorjs/image": "^2.3.3",
|
||||
"@editorjs/editorjs": "^2.17.0",
|
||||
"@editorjs/header": "^2.4.1",
|
||||
"@editorjs/image": "^2.3.4",
|
||||
"@editorjs/inline-code": "^1.3.1",
|
||||
"@editorjs/list": "^1.4.0",
|
||||
"@editorjs/marker": "^1.2.2",
|
||||
"@editorjs/table": "^1.2.0",
|
||||
"@editorjs/table": "^1.2.2",
|
||||
"@editorjs/warning": "^1.1.1",
|
||||
"autoprefixer": "^9.1.3",
|
||||
"babel": "^6.23.0",
|
||||
|
@ -61,8 +63,8 @@
|
|||
"chai-http": "^4.0.0",
|
||||
"css-loader": "^1.0.0",
|
||||
"cssnano": "^4.1.0",
|
||||
"eslint": "^5.3.0",
|
||||
"eslint-config-codex": "github:codex-team/eslint-config",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-codex": "^1.3.4",
|
||||
"eslint-plugin-chai-friendly": "^0.4.1",
|
||||
"eslint-plugin-import": "^2.14.0",
|
||||
"eslint-plugin-node": "^8.0.1",
|
||||
|
|
2
public/dist/code-styling.bundle.js
vendored
2
public/dist/code-styling.bundle.js
vendored
File diff suppressed because one or more lines are too long
2
public/dist/code-styling.css
vendored
2
public/dist/code-styling.css
vendored
|
@ -1,2 +1,2 @@
|
|||
.hljs{display:block;background:#fff;padding:.5em;color:#333;overflow-x:auto}.hljs-comment,.hljs-meta{color:#969896}.hljs-emphasis,.hljs-quote,.hljs-string,.hljs-strong,.hljs-template-variable,.hljs-variable{color:#df5000}.hljs-keyword,.hljs-selector-tag,.hljs-type{color:#a71d5d}.hljs-attribute,.hljs-bullet,.hljs-literal,.hljs-symbol{color:#0086b3}.hljs-name,.hljs-section{color:#63a35c}.hljs-tag{color:#333}.hljs-attr,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-title{color:#795da3}.hljs-addition{color:#55a532;background-color:#eaffea}.hljs-deletion{color:#bd2c00;background-color:#ffecec}.hljs-link{text-decoration:underline}
|
||||
.hljs{display:block;background:#fff;padding:.5em;color:#333;overflow-x:auto}.hljs-comment,.hljs-meta{color:#969896}.hljs-emphasis,.hljs-quote,.hljs-strong,.hljs-template-variable,.hljs-variable{color:#df5000}.hljs-keyword,.hljs-selector-tag,.hljs-type{color:#d73a49}.hljs-attribute,.hljs-bullet,.hljs-literal,.hljs-symbol{color:#0086b3}.hljs-name,.hljs-section{color:#63a35c}.hljs-tag{color:#333}.hljs-attr,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-title{color:#6f42c1}.hljs-addition{color:#55a532;background-color:#eaffea}.hljs-deletion{color:#bd2c00;background-color:#ffecec}.hljs-link{text-decoration:underline}.hljs-number{color:#005cc5}.hljs-string{color:#032f62}
|
||||
.diff{display:inline-block;width:100%}.diff span{color:inherit!important}.diff--added{color:#277030;background-color:#e2fce7}.diff--added:before{content:"+";opacity:.4}.diff--removed{color:#ae363c;background-color:#ffe6e6}.diff--removed:before{content:"-";opacity:.4}
|
||||
|
|
71
public/dist/editor.bundle.js
vendored
71
public/dist/editor.bundle.js
vendored
File diff suppressed because one or more lines are too long
4
public/dist/main.bundle.js
vendored
4
public/dist/main.bundle.js
vendored
File diff suppressed because one or more lines are too long
2
public/dist/main.css
vendored
2
public/dist/main.css
vendored
File diff suppressed because one or more lines are too long
|
@ -6,7 +6,6 @@ const Alias = require('../models/alias');
|
|||
*/
|
||||
class Aliases {
|
||||
/**
|
||||
* @static
|
||||
* Find and return entity with given alias
|
||||
*
|
||||
* @param {string} aliasName - alias name of entity
|
||||
|
|
|
@ -7,7 +7,6 @@ const Alias = require('../models/alias');
|
|||
*/
|
||||
class Pages {
|
||||
/**
|
||||
* @static
|
||||
* Fields required for page model creation
|
||||
*
|
||||
* @returns {['title', 'body']}
|
||||
|
@ -17,7 +16,6 @@ class Pages {
|
|||
}
|
||||
|
||||
/**
|
||||
* @static
|
||||
* Find and return page model with passed id
|
||||
*
|
||||
* @param {string} id - page id
|
||||
|
@ -43,20 +41,18 @@ class Pages {
|
|||
}
|
||||
|
||||
/**
|
||||
* @static
|
||||
* Return all pages without children of passed page
|
||||
*
|
||||
* @param {string} parent - id of current page
|
||||
* @returns {Promise<Page[]>}
|
||||
*/
|
||||
static async getAllExceptChildren(parent) {
|
||||
let pagesAvailable = this.removeChildren(await Pages.getAll(), parent);
|
||||
const pagesAvailable = this.removeChildren(await Pages.getAll(), parent);
|
||||
|
||||
return pagesAvailable.filter((item) => item !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @static
|
||||
* Set all children elements to null
|
||||
*
|
||||
* @param {Page[]} [pagesAvailable] - Array of all pages
|
||||
|
@ -71,6 +67,7 @@ class Pages {
|
|||
pagesAvailable[index] = null;
|
||||
pagesAvailable = Pages.removeChildren(pagesAvailable, item._id);
|
||||
});
|
||||
|
||||
return pagesAvailable;
|
||||
}
|
||||
|
||||
|
@ -91,7 +88,7 @@ class Pages {
|
|||
if (insertedPage.uri) {
|
||||
const alias = new Alias({
|
||||
id: insertedPage._id,
|
||||
type: Alias.types.PAGE
|
||||
type: Alias.types.PAGE,
|
||||
}, insertedPage.uri);
|
||||
|
||||
alias.save();
|
||||
|
@ -161,7 +158,7 @@ class Pages {
|
|||
if (updatedPage.uri) {
|
||||
const alias = new Alias({
|
||||
id: updatedPage._id,
|
||||
type: Alias.types.PAGE
|
||||
type: Alias.types.PAGE,
|
||||
}, updatedPage.uri);
|
||||
|
||||
alias.save();
|
||||
|
|
|
@ -70,8 +70,8 @@ class PagesOrder {
|
|||
* @param {Page[]} pages - list of all available pages
|
||||
* @param {string} currentPageId - page's id around which we are ordering
|
||||
* @param {string} parentPageId - parent page's id that contains page above
|
||||
* @param {Boolean} ignoreSelf - should we ignore current page in list or not
|
||||
* @return {Page[]}
|
||||
* @param {boolean} ignoreSelf - should we ignore current page in list or not
|
||||
* @returns {Page[]}
|
||||
*/
|
||||
static async getOrderedChildren(pages, currentPageId, parentPageId, ignoreSelf = false) {
|
||||
const children = await Model.get(parentPageId);
|
||||
|
|
|
@ -17,6 +17,7 @@ const config = require('../../config');
|
|||
class Transport {
|
||||
/**
|
||||
* Saves file passed from client
|
||||
*
|
||||
* @param {object} multerData - file data from multer
|
||||
* @param {string} multerData.originalname - original name of the file
|
||||
* @param {string} multerData.filename - name of the uploaded file
|
||||
|
@ -25,12 +26,18 @@ class Transport {
|
|||
* @param {string} multerData.mimetype - MIME type of the uploaded file
|
||||
*
|
||||
* @param {object} map - object that represents how should fields of File object should be mapped to response
|
||||
* @return {Promise<FileData>}
|
||||
* @returns {Promise<FileData>}
|
||||
*/
|
||||
static async save(multerData, map) {
|
||||
const { originalname: name, path, filename, size, mimetype } = multerData;
|
||||
|
||||
const file = new Model({ name, filename, path, size, mimetype });
|
||||
const file = new Model({
|
||||
name,
|
||||
filename,
|
||||
path,
|
||||
size,
|
||||
mimetype,
|
||||
});
|
||||
|
||||
await file.save();
|
||||
|
||||
|
@ -45,9 +52,10 @@ class Transport {
|
|||
|
||||
/**
|
||||
* Fetches file by passed URL
|
||||
*
|
||||
* @param {string} url - URL of the file
|
||||
* @param {object} map - object that represents how should fields of File object should be mapped to response
|
||||
* @return {Promise<FileData>}
|
||||
* @returns {Promise<FileData>}
|
||||
*/
|
||||
static async fetch(url, map) {
|
||||
const fetchedFile = await fetch(url);
|
||||
|
@ -64,7 +72,7 @@ class Transport {
|
|||
filename: `${filename}.${ext}`,
|
||||
path: `${config.uploads}/${filename}.${ext}`,
|
||||
size: buffer.length,
|
||||
mimetype: type ? type.mime : fetchedFile.headers.get('content-type')
|
||||
mimetype: type ? type.mime : fetchedFile.headers.get('content-type'),
|
||||
});
|
||||
|
||||
await file.save();
|
||||
|
@ -94,11 +102,12 @@ class Transport {
|
|||
|
||||
if (fields.length > 1) {
|
||||
let object = {};
|
||||
let result = object;
|
||||
const result = object;
|
||||
|
||||
fields.forEach((field, i) => {
|
||||
if (i === fields.length - 1) {
|
||||
object[field] = data[name];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ const Model = require('../models/user');
|
|||
*/
|
||||
class Users {
|
||||
/**
|
||||
* @static
|
||||
* Find and return user model.
|
||||
*
|
||||
* @returns {Promise<User>}
|
||||
|
|
|
@ -10,6 +10,7 @@ import List from '@editorjs/list';
|
|||
import Delimiter from '@editorjs/delimiter';
|
||||
import Table from '@editorjs/table';
|
||||
import Warning from '@editorjs/warning';
|
||||
import Checklist from '@editorjs/checklist';
|
||||
|
||||
/**
|
||||
* Inline Tools for the Editor
|
||||
|
@ -80,6 +81,11 @@ export default class Editor {
|
|||
inlineToolbar: true
|
||||
},
|
||||
|
||||
checklist: {
|
||||
class: Checklist,
|
||||
inlineToolbar: true,
|
||||
},
|
||||
|
||||
/**
|
||||
* Inline Tools
|
||||
*/
|
||||
|
|
|
@ -362,3 +362,59 @@
|
|||
padding-left: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checklist
|
||||
* ==================
|
||||
*/
|
||||
.block-checklist {
|
||||
margin: 20px 0;
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
box-sizing: content-box;
|
||||
align-items: center;
|
||||
|
||||
&-checkbox {
|
||||
display: inline-block;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: 0 10px 0 0;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #d0d0d0;
|
||||
background: #fff;
|
||||
user-select: none;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
width: 8px;
|
||||
height: 5px;
|
||||
border: 2px solid #fcfff4;
|
||||
border-top: none;
|
||||
border-right: none;
|
||||
background: transparent;
|
||||
content: '';
|
||||
opacity: 0;
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
&--checked {
|
||||
background: #388ae5;
|
||||
border-color: #388ae5;
|
||||
}
|
||||
}
|
||||
|
||||
&-text {
|
||||
outline: none;
|
||||
flex-grow: 1;
|
||||
padding: 5px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.block-checklist__item-checkbox--checked, .block-checklist__item-checkbox::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ const { aliases: aliasesDb } = require('../utils/database/index');
|
|||
const { binaryMD5 } = require('../utils/crypto');
|
||||
|
||||
/**
|
||||
* @typedef {Object} AliasData
|
||||
* @typedef {object} AliasData
|
||||
* @property {string} _id - alias id
|
||||
* @property {string} hash - alias binary hash
|
||||
* @property {string} type - entity type
|
||||
|
@ -25,22 +25,26 @@ class Alias {
|
|||
/**
|
||||
* Return Alias types
|
||||
*
|
||||
* @returns {Object}
|
||||
* @returns {object}
|
||||
*/
|
||||
static get types() {
|
||||
return {
|
||||
PAGE: 'page'
|
||||
PAGE: 'page',
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Find and return alias with given alias
|
||||
*
|
||||
* @param {string} aliasName - alias of entity
|
||||
* @returns {Promise<Alias>}
|
||||
*/
|
||||
static async get(aliasName) {
|
||||
const hash = binaryMD5(aliasName);
|
||||
let data = await aliasesDb.findOne({ hash: hash, deprecated: false });
|
||||
let data = await aliasesDb.findOne({
|
||||
hash: hash,
|
||||
deprecated: false,
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
data = await aliasesDb.findOne({ hash: hash });
|
||||
|
@ -50,7 +54,7 @@ class Alias {
|
|||
}
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @class
|
||||
*
|
||||
* @param {AliasData} data
|
||||
* @param {string} aliasName - alias of entity
|
||||
|
@ -110,12 +114,13 @@ class Alias {
|
|||
id: this.id,
|
||||
type: this.type,
|
||||
hash: this.hash,
|
||||
deprecated: this.deprecated
|
||||
deprecated: this.deprecated,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark alias as deprecated
|
||||
*
|
||||
* @param {string} aliasName - alias of entity
|
||||
* @returns {Promise<Alias>}
|
||||
*/
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const { files: filesDb } = require('../utils/database/index');
|
||||
|
||||
/**
|
||||
* @typedef {Object} FileData
|
||||
* @typedef {object} FileData
|
||||
*
|
||||
* @property {string} _id - file id
|
||||
* @property {string} name - original file name
|
||||
|
@ -25,6 +25,7 @@ const { files: filesDb } = require('../utils/database/index');
|
|||
class File {
|
||||
/**
|
||||
* Find and return model of file with given id
|
||||
*
|
||||
* @param {string} _id - file id
|
||||
* @returns {Promise<File>}
|
||||
*/
|
||||
|
@ -36,6 +37,7 @@ class File {
|
|||
|
||||
/**
|
||||
* Find and return model of file with given id
|
||||
*
|
||||
* @param {string} filename - uploaded filename
|
||||
* @returns {Promise<File>}
|
||||
*/
|
||||
|
@ -48,7 +50,7 @@ class File {
|
|||
/**
|
||||
* Find all files which match passed query object
|
||||
*
|
||||
* @param {Object} query
|
||||
* @param {object} query
|
||||
* @returns {Promise<File[]>}
|
||||
*/
|
||||
static async getAll(query = {}) {
|
||||
|
@ -58,7 +60,7 @@ class File {
|
|||
}
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @class
|
||||
*
|
||||
* @param {FileData} data
|
||||
*/
|
||||
|
@ -101,7 +103,7 @@ class File {
|
|||
filename: this.filename,
|
||||
path: this.path,
|
||||
mimetype: this.mimetype,
|
||||
size: this.size
|
||||
size: this.size,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -137,8 +139,9 @@ class File {
|
|||
|
||||
/**
|
||||
* Removes unnecessary public folder prefix
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {string}
|
||||
* @returns {string}
|
||||
*/
|
||||
processPath(path) {
|
||||
return path.replace(/^public/, '');
|
||||
|
|
|
@ -2,7 +2,7 @@ const urlify = require('../utils/urlify');
|
|||
const { pages: pagesDb } = require('../utils/database/index');
|
||||
|
||||
/**
|
||||
* @typedef {Object} PageData
|
||||
* @typedef {object} PageData
|
||||
* @property {string} _id - page id
|
||||
* @property {string} title - page title
|
||||
* @property {string} uri - page uri
|
||||
|
@ -23,6 +23,7 @@ const { pages: pagesDb } = require('../utils/database/index');
|
|||
class Page {
|
||||
/**
|
||||
* Find and return model of page with given id
|
||||
*
|
||||
* @param {string} _id - page id
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
|
@ -34,6 +35,7 @@ class Page {
|
|||
|
||||
/**
|
||||
* Find and return model of page with given uri
|
||||
*
|
||||
* @param {string} uri - page uri
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
|
@ -46,7 +48,7 @@ class Page {
|
|||
/**
|
||||
* Find all pages which match passed query object
|
||||
*
|
||||
* @param {Object} query
|
||||
* @param {object} query
|
||||
* @returns {Promise<Page[]>}
|
||||
*/
|
||||
static async getAll(query = {}) {
|
||||
|
@ -56,7 +58,7 @@ class Page {
|
|||
}
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @class
|
||||
*
|
||||
* @param {PageData} data
|
||||
*/
|
||||
|
@ -89,7 +91,7 @@ class Page {
|
|||
/**
|
||||
* Return PageData object
|
||||
*
|
||||
* @return {PageData}
|
||||
* @returns {PageData}
|
||||
*/
|
||||
get data() {
|
||||
return {
|
||||
|
@ -97,13 +99,14 @@ class Page {
|
|||
title: this.title,
|
||||
uri: this.uri,
|
||||
body: this.body,
|
||||
parent: this._parent
|
||||
parent: this._parent,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract first header from editor data
|
||||
* @return {string}
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
extractTitleFromBody() {
|
||||
const headerBlock = this.body ? this.body.blocks.find(block => block.type === 'header') : '';
|
||||
|
@ -113,7 +116,8 @@ class Page {
|
|||
|
||||
/**
|
||||
* Transform title for uri
|
||||
* @return {string}
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
transformTitleToUri() {
|
||||
return urlify(this.title);
|
||||
|
@ -184,6 +188,7 @@ class Page {
|
|||
* Find and return available uri
|
||||
*
|
||||
* @returns {Promise<string>}
|
||||
* @param uri
|
||||
*/
|
||||
async composeUri(uri) {
|
||||
let pageWithSameUriCount = 0;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const { pagesOrder: db } = require('../utils/database/index');
|
||||
|
||||
/**
|
||||
* @typedef {Object} PageOrderData
|
||||
* @typedef {object} PageOrderData
|
||||
* @property {string} _id - row unique id
|
||||
* @property {string} page - page id
|
||||
* @property {Array<string>} order - list of ordered pages
|
||||
|
@ -37,7 +37,7 @@ class PageOrder {
|
|||
/**
|
||||
* Find all pages which match passed query object
|
||||
*
|
||||
* @param {Object} query
|
||||
* @param {object} query
|
||||
* @returns {Promise<Page[]>}
|
||||
*/
|
||||
static async getAll(query = {}) {
|
||||
|
@ -47,7 +47,7 @@ class PageOrder {
|
|||
}
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @class
|
||||
*
|
||||
* @param {PageOrderData} data
|
||||
*/
|
||||
|
@ -65,6 +65,7 @@ class PageOrder {
|
|||
|
||||
/**
|
||||
* constructor data setter
|
||||
*
|
||||
* @param {PageOrderData} pageOrderData
|
||||
*/
|
||||
set data(pageOrderData) {
|
||||
|
@ -74,13 +75,14 @@ class PageOrder {
|
|||
|
||||
/**
|
||||
* Return Page Children order
|
||||
*
|
||||
* @returns {PageOrderData}
|
||||
*/
|
||||
get data() {
|
||||
return {
|
||||
_id: this._id,
|
||||
page: '' + this._page,
|
||||
order: this._order
|
||||
order: this._order,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -176,7 +178,7 @@ class PageOrder {
|
|||
/**
|
||||
* Returns ordered list
|
||||
*
|
||||
* @return {string[]}
|
||||
* @returns {string[]}
|
||||
*/
|
||||
get order() {
|
||||
return this._order;
|
||||
|
|
|
@ -24,9 +24,9 @@ class User {
|
|||
}
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @class
|
||||
*
|
||||
* @param {Object} userData
|
||||
* @param {object} userData
|
||||
*/
|
||||
constructor(userData) {
|
||||
this.passHash = userData.passHash;
|
||||
|
|
|
@ -23,19 +23,20 @@ router.get('*', verifyToken, async (req, res) => {
|
|||
|
||||
switch (alias.type) {
|
||||
case Alias.types.PAGE: {
|
||||
let page = await Pages.get(alias.id);
|
||||
const page = await Pages.get(alias.id);
|
||||
|
||||
let pageParent = await page.parent;
|
||||
const pageParent = await page.parent;
|
||||
|
||||
res.render('pages/page', {
|
||||
page, pageParent
|
||||
page,
|
||||
pageParent,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err.message
|
||||
error: err.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -16,7 +16,7 @@ const parseForm = express.urlencoded({ extended: false });
|
|||
router.get('/auth', csrfProtection, function (req, res) {
|
||||
res.render('auth', {
|
||||
title: 'Login page',
|
||||
csrfToken: req.csrfToken()
|
||||
csrfToken: req.csrfToken(),
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -24,13 +24,13 @@ router.get('/auth', csrfProtection, function (req, res) {
|
|||
* Process given password
|
||||
*/
|
||||
router.post('/auth', parseForm, csrfProtection, async (req, res) => {
|
||||
let userDoc = await Users.get();
|
||||
const userDoc = await Users.get();
|
||||
|
||||
if (!userDoc) {
|
||||
res.render('auth', {
|
||||
title: 'Login page',
|
||||
header: 'Password not set',
|
||||
csrfToken: req.csrfToken()
|
||||
csrfToken: req.csrfToken(),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -41,14 +41,14 @@ router.post('/auth', parseForm, csrfProtection, async (req, res) => {
|
|||
res.render('auth', {
|
||||
title: 'Login page',
|
||||
header: 'Wrong password',
|
||||
csrfToken: req.csrfToken()
|
||||
csrfToken: req.csrfToken(),
|
||||
});
|
||||
}
|
||||
|
||||
const token = jwt.sign({
|
||||
'iss': 'Codex Team',
|
||||
'sub': 'auth',
|
||||
'iat': Date.now()
|
||||
iss: 'Codex Team',
|
||||
sub: 'auth',
|
||||
iat: Date.now(),
|
||||
}, passHash + config.secret);
|
||||
|
||||
res.cookie('authToken', token, { httpOnly: true });
|
||||
|
|
|
@ -10,11 +10,11 @@ const allowEdit = require('./middlewares/locals');
|
|||
* Create new page form
|
||||
*/
|
||||
router.get('/page/new', verifyToken, allowEdit, async (req, res, next) => {
|
||||
let pagesAvailable = await Pages.getAll();
|
||||
const pagesAvailable = await Pages.getAll();
|
||||
|
||||
res.render('pages/form', {
|
||||
pagesAvailable,
|
||||
page: null
|
||||
page: null,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -32,7 +32,7 @@ router.get('/page/edit/:id', verifyToken, allowEdit, async (req, res, next) => {
|
|||
res.render('pages/form', {
|
||||
page,
|
||||
parentsChildrenOrdered,
|
||||
pagesAvailable
|
||||
pagesAvailable,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(404);
|
||||
|
@ -47,12 +47,13 @@ router.get('/page/:id', verifyToken, async (req, res, next) => {
|
|||
const pageId = req.params.id;
|
||||
|
||||
try {
|
||||
let page = await Pages.get(pageId);
|
||||
const page = await Pages.get(pageId);
|
||||
|
||||
let pageParent = await page.parent;
|
||||
const pageParent = await page.parent;
|
||||
|
||||
res.render('pages/page', {
|
||||
page, pageParent
|
||||
page,
|
||||
pageParent,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(404);
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/**
|
||||
* Helper for making async middlewares for express router
|
||||
*
|
||||
* @param fn
|
||||
* @return {function(*=, *=, *=)}
|
||||
* @returns {function(*=, *=, *=)}
|
||||
*/
|
||||
module.exports = function asyncMiddleware(fn) {
|
||||
return (req, res, next) => {
|
||||
|
|
|
@ -2,6 +2,7 @@ const crypto = require('crypto');
|
|||
|
||||
/**
|
||||
* Create binary md5
|
||||
*
|
||||
* @param stringToHash - string to hash
|
||||
* @returns {string} - binary hash of argument
|
||||
*/
|
||||
|
@ -13,7 +14,8 @@ function binaryMD5(stringToHash) {
|
|||
|
||||
/**
|
||||
* Returns 16 random bytes in hex format
|
||||
* @return {Promise<string>}
|
||||
*
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function random16() {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -29,5 +31,5 @@ function random16() {
|
|||
|
||||
module.exports = {
|
||||
binaryMD5,
|
||||
random16
|
||||
random16,
|
||||
};
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
/**
|
||||
* Merge to objects recursively
|
||||
*
|
||||
* @param {object} target
|
||||
* @param {object[]} sources
|
||||
* @return {object}
|
||||
* @returns {object}
|
||||
*/
|
||||
function deepMerge(target, ...sources) {
|
||||
const isObject = item => item && typeof item === 'object' && !Array.isArray(item);
|
||||
|
||||
if (!sources.length) return target;
|
||||
if (!sources.length) {
|
||||
return target;
|
||||
}
|
||||
const source = sources.shift();
|
||||
|
||||
if (isObject(target) && isObject(source)) {
|
||||
|
@ -28,5 +31,5 @@ function deepMerge(target, ...sources) {
|
|||
}
|
||||
|
||||
module.exports = {
|
||||
deepMerge
|
||||
deepMerge,
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ const config = require('../../config');
|
|||
const rcPath = path.resolve(__dirname, '../../', config.rcFile || './.codexdocsrc');
|
||||
|
||||
/**
|
||||
* @typedef {Object} RCData
|
||||
* @typedef {object} RCData
|
||||
* @property {string} title - website title
|
||||
* @property {object[]} menu - options for website menu
|
||||
* @property {string} menu[].title - menu option title
|
||||
|
@ -20,12 +20,12 @@ module.exports = class RCParser {
|
|||
* Default CodeX Docs configuration
|
||||
*
|
||||
* @static
|
||||
* @return {{title: string, menu: Array}}
|
||||
* @returns {{title: string, menu: Array}}
|
||||
*/
|
||||
static get DEFAULTS() {
|
||||
return {
|
||||
title: 'CodeX Docs',
|
||||
menu: []
|
||||
menu: [],
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ module.exports = class RCParser {
|
|||
* Find and parse runtime configuration file
|
||||
*
|
||||
* @static
|
||||
* @return {{title: string, menu: []}}
|
||||
* @returns {{title: string, menu: []}}
|
||||
*/
|
||||
static getConfiguration() {
|
||||
if (!fs.existsSync(rcPath)) {
|
||||
|
@ -48,11 +48,12 @@ module.exports = class RCParser {
|
|||
userConfig = JSON.parse(file);
|
||||
} catch (e) {
|
||||
console.log('CodeX Docs rc file should be in JSON format.');
|
||||
|
||||
return RCParser.DEFAULTS;
|
||||
}
|
||||
|
||||
for (let option in userConfig) {
|
||||
if (userConfig.hasOwnProperty(option)) {
|
||||
for (const option in userConfig) {
|
||||
if (Object.prototype.hasOwnProperty.call(userConfig, option)) {
|
||||
rConfig[option] = userConfig[option] || RCParser.DEFAULTS[option] || undefined;
|
||||
}
|
||||
}
|
||||
|
@ -70,6 +71,7 @@ module.exports = class RCParser {
|
|||
|
||||
if (!option || option instanceof Array || typeof option !== 'object') {
|
||||
console.log(`Menu option #${i} in rc file must be a string or an object`);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -77,11 +79,13 @@ module.exports = class RCParser {
|
|||
|
||||
if (!title || typeof title !== 'string') {
|
||||
console.log(`Menu option #${i} title must be a string.`);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!uri || typeof uri !== 'string') {
|
||||
console.log(`Menu option #${i} uri must be a string.`);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -93,7 +97,7 @@ module.exports = class RCParser {
|
|||
return {
|
||||
title: option,
|
||||
/* Replace all non alpha- and numeric-symbols with '-' */
|
||||
uri: '/' + option.toLowerCase().replace(/[ -/:-@[-`{-~]+/, '-')
|
||||
uri: '/' + option.toLowerCase().replace(/[ -/:-@[-`{-~]+/, '-'),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,70 +1,70 @@
|
|||
const translationTable = {
|
||||
'а': 'a',
|
||||
'б': 'b',
|
||||
'в': 'v',
|
||||
'г': 'g',
|
||||
'д': 'd',
|
||||
'е': 'e',
|
||||
'ж': 'g',
|
||||
'з': 'z',
|
||||
'и': 'i',
|
||||
'й': 'y',
|
||||
'к': 'k',
|
||||
'л': 'l',
|
||||
'м': 'm',
|
||||
'н': 'n',
|
||||
'о': 'o',
|
||||
'п': 'p',
|
||||
'р': 'r',
|
||||
'с': 's',
|
||||
'т': 't',
|
||||
'у': 'u',
|
||||
'ф': 'f',
|
||||
'ы': 'i',
|
||||
'э': 'e',
|
||||
'А': 'A',
|
||||
'Б': 'B',
|
||||
'В': 'V',
|
||||
'Г': 'G',
|
||||
'Д': 'D',
|
||||
'Е': 'E',
|
||||
'Ж': 'G',
|
||||
'З': 'Z',
|
||||
'И': 'I',
|
||||
'Й': 'Y',
|
||||
'К': 'K',
|
||||
'Л': 'L',
|
||||
'М': 'M',
|
||||
'Н': 'N',
|
||||
'О': 'O',
|
||||
'П': 'P',
|
||||
'Р': 'R',
|
||||
'С': 'S',
|
||||
'Т': 'T',
|
||||
'У': 'U',
|
||||
'Ф': 'F',
|
||||
'Ы': 'I',
|
||||
'Э': 'E',
|
||||
'ё': 'yo',
|
||||
'х': 'h',
|
||||
'ц': 'ts',
|
||||
'ч': 'ch',
|
||||
'ш': 'sh',
|
||||
'щ': 'shch',
|
||||
'ъ': "''",
|
||||
'ь': "'",
|
||||
'ю': 'yu',
|
||||
'я': 'ya',
|
||||
'Ё': 'YO',
|
||||
'Х': 'H',
|
||||
'Ц': 'TS',
|
||||
'Ч': 'CH',
|
||||
'Ш': 'SH',
|
||||
'Щ': 'SHCH',
|
||||
'Ъ': "''",
|
||||
'Ь': "'",
|
||||
'Ю': 'YU',
|
||||
'Я': 'YA'
|
||||
а: 'a',
|
||||
б: 'b',
|
||||
в: 'v',
|
||||
г: 'g',
|
||||
д: 'd',
|
||||
е: 'e',
|
||||
ж: 'g',
|
||||
з: 'z',
|
||||
и: 'i',
|
||||
й: 'y',
|
||||
к: 'k',
|
||||
л: 'l',
|
||||
м: 'm',
|
||||
н: 'n',
|
||||
о: 'o',
|
||||
п: 'p',
|
||||
р: 'r',
|
||||
с: 's',
|
||||
т: 't',
|
||||
у: 'u',
|
||||
ф: 'f',
|
||||
ы: 'i',
|
||||
э: 'e',
|
||||
А: 'A',
|
||||
Б: 'B',
|
||||
В: 'V',
|
||||
Г: 'G',
|
||||
Д: 'D',
|
||||
Е: 'E',
|
||||
Ж: 'G',
|
||||
З: 'Z',
|
||||
И: 'I',
|
||||
Й: 'Y',
|
||||
К: 'K',
|
||||
Л: 'L',
|
||||
М: 'M',
|
||||
Н: 'N',
|
||||
О: 'O',
|
||||
П: 'P',
|
||||
Р: 'R',
|
||||
С: 'S',
|
||||
Т: 'T',
|
||||
У: 'U',
|
||||
Ф: 'F',
|
||||
Ы: 'I',
|
||||
Э: 'E',
|
||||
ё: 'yo',
|
||||
х: 'h',
|
||||
ц: 'ts',
|
||||
ч: 'ch',
|
||||
ш: 'sh',
|
||||
щ: 'shch',
|
||||
ъ: "''",
|
||||
ь: "'",
|
||||
ю: 'yu',
|
||||
я: 'ya',
|
||||
Ё: 'YO',
|
||||
Х: 'H',
|
||||
Ц: 'TS',
|
||||
Ч: 'CH',
|
||||
Ш: 'SH',
|
||||
Щ: 'SHCH',
|
||||
Ъ: "''",
|
||||
Ь: "'",
|
||||
Ю: 'YU',
|
||||
Я: 'YA',
|
||||
};
|
||||
/**
|
||||
* Function to translate string
|
||||
|
|
|
@ -13,7 +13,7 @@ module.exports = (function () {
|
|||
*
|
||||
* @example svg('path/from/root/dir')
|
||||
* @param filename - name of icon
|
||||
* @returns {String} - svg code
|
||||
* @returns {string} - svg code
|
||||
*/
|
||||
twig.extendFunction('svg', function (filename) {
|
||||
return fs.readFileSync(`${__dirname}/../frontend/svg/${filename}.svg`, 'utf-8');
|
||||
|
|
12
src/views/pages/blocks/checklist.twig
Normal file
12
src/views/pages/blocks/checklist.twig
Normal file
|
@ -0,0 +1,12 @@
|
|||
<div class="block-checklist">
|
||||
{% for item in items %}
|
||||
<div class="block-checklist__item">
|
||||
{% if item.checked %}
|
||||
<span class="block-checklist__item-checkbox block-checklist__item-checkbox--checked"></span>
|
||||
{% else %}
|
||||
<span class="block-checklist__item-checkbox"></span>
|
||||
{% endif %}
|
||||
<div class="block-checklist__item-text">{{ item.text }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
|
@ -32,7 +32,7 @@
|
|||
{% for block in page.body.blocks %}
|
||||
{# Skip first header, because it is already showed as a Title #}
|
||||
{% if not (loop.first and block.type == 'header') %}
|
||||
{% if block.type in ['paragraph', 'header', 'image', 'code', 'list', 'delimiter', 'table', 'warning'] %}
|
||||
{% if block.type in ['paragraph', 'header', 'image', 'code', 'list', 'delimiter', 'table', 'warning', 'checklist'] %}
|
||||
{% include './blocks/' ~ block.type ~ '.twig' with block.data %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue