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