mirror of
https://github.com/codex-team/codex.docs.git
synced 2025-08-09 07:25:21 +02:00
Added abiltity to support few pages with same title
This commit is contained in:
parent
88172e7d34
commit
77f671b052
8 changed files with 185 additions and 34 deletions
|
@ -12,6 +12,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/polyfill": "^7.0.0",
|
"@babel/polyfill": "^7.0.0",
|
||||||
|
"blueimp-md5": "^2.10.0",
|
||||||
"body-parser": "latest",
|
"body-parser": "latest",
|
||||||
"codex.editor": "^2.1.3",
|
"codex.editor": "^2.1.3",
|
||||||
"codex.editor.header": "^2.0.5",
|
"codex.editor.header": "^2.0.5",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const Model = require('../models/page');
|
const Model = require('../models/page');
|
||||||
const Alias = require('../models/alias');
|
const Alias = require('../models/alias');
|
||||||
const aliasTypes = require('../constants/aliasTypes');
|
const aliasTypes = require('../constants/aliasTypes');
|
||||||
const getHashFromString = require('../utils/hash');
|
const md5 = require('blueimp-md5');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class Pages
|
* @class Pages
|
||||||
|
@ -15,7 +15,7 @@ class Pages {
|
||||||
* @returns {['title', 'body']}
|
* @returns {['title', 'body']}
|
||||||
*/
|
*/
|
||||||
static get REQUIRED_FIELDS() {
|
static get REQUIRED_FIELDS() {
|
||||||
return [ 'body' ];
|
return ['body'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -53,20 +53,18 @@ class Pages {
|
||||||
static async insert(data) {
|
static async insert(data) {
|
||||||
try {
|
try {
|
||||||
Pages.validate(data);
|
Pages.validate(data);
|
||||||
console.log('data', data);
|
|
||||||
const page = new Model(data);
|
const page = new Model(data);
|
||||||
|
|
||||||
const pagePromise = page.save();
|
const pagePromise = page.save();
|
||||||
|
const updatedPage = await pagePromise;
|
||||||
|
|
||||||
pagePromise.then(() => {
|
|
||||||
const alias = new Alias({
|
const alias = new Alias({
|
||||||
id: page._id,
|
id: updatedPage._id,
|
||||||
type: aliasTypes.PAGE,
|
type: aliasTypes.PAGE,
|
||||||
hash: getHashFromString(page.uri).toString()
|
hash: md5(updatedPage.uri)
|
||||||
});
|
});
|
||||||
|
|
||||||
alias.save();
|
alias.save();
|
||||||
});
|
|
||||||
|
|
||||||
return pagePromise;
|
return pagePromise;
|
||||||
} catch (validationError) {
|
} catch (validationError) {
|
||||||
|
@ -119,10 +117,22 @@ class Pages {
|
||||||
if (!page._id) {
|
if (!page._id) {
|
||||||
throw new Error('Page with given id does not exist');
|
throw new Error('Page with given id does not exist');
|
||||||
}
|
}
|
||||||
|
const oldAlias = page.uri;
|
||||||
|
|
||||||
page.data = data;
|
page.data = data;
|
||||||
|
const pagePromise = page.save();
|
||||||
|
const updatedPage = await pagePromise;
|
||||||
|
|
||||||
return page.save();
|
Alias.markAsDeprecated(oldAlias);
|
||||||
|
|
||||||
|
const alias = new Alias({
|
||||||
|
id: updatedPage._id,
|
||||||
|
type: aliasTypes.PAGE,
|
||||||
|
hash: md5(updatedPage.uri)
|
||||||
|
});
|
||||||
|
|
||||||
|
alias.save();
|
||||||
|
return pagePromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
const {aliases: aliasesDb} = require('../utils/database/index');
|
const {aliases: aliasesDb} = require('../utils/database/index');
|
||||||
const getHashFromString = require('../utils/hash');
|
const md5 = require('blueimp-md5');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} AliasData
|
* @typedef {Object} AliasData
|
||||||
* @property {string} _id - alias id
|
* @property {string} _id - alias id
|
||||||
* @property {string} hash - alias hash
|
* @property {string} hash - alias hash
|
||||||
* @property {string} type - entity type
|
* @property {string} type - entity type
|
||||||
|
* @property {boolean} deprecated - indicate if alias deprecated
|
||||||
* @property {string} id - entity id
|
* @property {string} id - entity id
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -15,6 +15,7 @@ const getHashFromString = require('../utils/hash');
|
||||||
* @property {string} _id - alias id
|
* @property {string} _id - alias id
|
||||||
* @property {string} hash - alias hash
|
* @property {string} hash - alias hash
|
||||||
* @property {string} type - entity type
|
* @property {string} type - entity type
|
||||||
|
* @property {boolean} deprecated - indicate if alias deprecated
|
||||||
* @property {string} id - entity title
|
* @property {string} id - entity title
|
||||||
*/
|
*/
|
||||||
class Alias {
|
class Alias {
|
||||||
|
@ -24,7 +25,7 @@ class Alias {
|
||||||
* @returns {Promise<Alias>}
|
* @returns {Promise<Alias>}
|
||||||
*/
|
*/
|
||||||
static async get(aliasName) {
|
static async get(aliasName) {
|
||||||
const hash = getHashFromString(aliasName).toString();
|
const hash = md5(aliasName);
|
||||||
const data = await aliasesDb.findOne({hash});
|
const data = await aliasesDb.findOne({hash});
|
||||||
|
|
||||||
return new Alias(data);
|
return new Alias(data);
|
||||||
|
@ -42,25 +43,65 @@ class Alias {
|
||||||
if (data._id) {
|
if (data._id) {
|
||||||
this._id = data._id;
|
this._id = data._id;
|
||||||
}
|
}
|
||||||
this.id = data.id;
|
this.data = data;
|
||||||
this.type = data.type;
|
|
||||||
this.hash = data.hash;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save or update page data in the database
|
* Save or update alias data in the database
|
||||||
*
|
*
|
||||||
* @returns {Promise<Alias>}
|
* @returns {Promise<Alias>}
|
||||||
*/
|
*/
|
||||||
async save() {
|
async save() {
|
||||||
if (!this._id) {
|
if (!this._id) {
|
||||||
const insertedRow = await aliasesDb.insert({id: this.id, type: this.type, hash: this.hash});
|
const insertedRow = await aliasesDb.insert(this.data);
|
||||||
} else {
|
} else {
|
||||||
await aliasesDb.update({_id: this._id}, this.data);
|
await aliasesDb.update({_id: this._id}, this.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set AliasData object fields to internal model fields
|
||||||
|
*
|
||||||
|
* @param {AliasData} aliasData
|
||||||
|
*/
|
||||||
|
set data(aliasData) {
|
||||||
|
const {id, type, hash, deprecated} = aliasData;
|
||||||
|
|
||||||
|
this.id = id || this.id;
|
||||||
|
this.type = type || this.type;
|
||||||
|
this.hash = hash || this.hash;
|
||||||
|
this.deprecated = deprecated || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return AliasData object
|
||||||
|
*
|
||||||
|
* @returns {AliasData}
|
||||||
|
*/
|
||||||
|
get data() {
|
||||||
|
return {
|
||||||
|
_id: this._id,
|
||||||
|
id: this.id,
|
||||||
|
type: this.type,
|
||||||
|
hash: this.hash,
|
||||||
|
deprecated: this.deprecated
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark alias as deprecated
|
||||||
|
* @param {string} aliasName - alias of entity
|
||||||
|
* @returns {Promise<Alias>}
|
||||||
|
*/
|
||||||
|
static async markAsDeprecated(aliasName) {
|
||||||
|
const alias = await Alias.get(aliasName);
|
||||||
|
|
||||||
|
alias.deprecated = true;
|
||||||
|
|
||||||
|
return alias.save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Alias;
|
module.exports = Alias;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const {pages: pagesDb} = require('../utils/database/index');
|
const {pages: pagesDb} = require('../utils/database/index');
|
||||||
const {aliases: aliasesDb} = require('../utils/database/index');
|
const {aliases: aliasesDb} = require('../utils/database/index');
|
||||||
|
const translateString = require('../utils/translation');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} PageData
|
* @typedef {Object} PageData
|
||||||
|
@ -33,6 +34,17 @@ class Page {
|
||||||
return new Page(data);
|
return new Page(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find and return model of page with given uri
|
||||||
|
* @param {string} uri - page uri
|
||||||
|
* @returns {Promise<Page>}
|
||||||
|
*/
|
||||||
|
static async getByUri(uri) {
|
||||||
|
const data = await pagesDb.findOne({uri});
|
||||||
|
|
||||||
|
return new Page(data);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find all pages which match passed query object
|
* Find all pages which match passed query object
|
||||||
*
|
*
|
||||||
|
@ -72,7 +84,7 @@ class Page {
|
||||||
|
|
||||||
this.body = body || this.body;
|
this.body = body || this.body;
|
||||||
this.title = this.extractTitleFromBody();
|
this.title = this.extractTitleFromBody();
|
||||||
this.uri = uri || this.title.toLowerCase().split(' ').join('-');
|
this.uri = uri || translateString(this.title.toLowerCase()).split(' ').join('-');
|
||||||
this._parent = parent || this._parent;
|
this._parent = parent || this._parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,15 +148,29 @@ class Page {
|
||||||
* @returns {Promise<Page>}
|
* @returns {Promise<Page>}
|
||||||
*/
|
*/
|
||||||
async save() {
|
async save() {
|
||||||
|
let newUri = translateString(this.title.toLowerCase()).split(' ').join('-');
|
||||||
|
let pageWithSameUri = await Page.getByUri(newUri);
|
||||||
|
let pageWithSameUriCount = 0;
|
||||||
|
|
||||||
|
while (pageWithSameUri._id) {
|
||||||
|
pageWithSameUriCount++;
|
||||||
|
pageWithSameUri = await Page.getByUri(newUri + `-${pageWithSameUriCount}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.uri = pageWithSameUriCount ? newUri + `-${pageWithSameUriCount}` : newUri;
|
||||||
|
|
||||||
if (!this._id) {
|
if (!this._id) {
|
||||||
const insertedRow = await pagesDb.insert({
|
const insertedRow = await pagesDb.insert({
|
||||||
...this.data,
|
...this.data,
|
||||||
uri: this.title.toLowerCase().split(' ').join('-')
|
uri: this.uri
|
||||||
});
|
});
|
||||||
|
|
||||||
this._id = insertedRow._id;
|
this._id = insertedRow._id;
|
||||||
} else {
|
} else {
|
||||||
await pagesDb.update({_id: this._id}, this.data);
|
await pagesDb.update({_id: this._id}, {
|
||||||
|
...this.data,
|
||||||
|
uri: this.uri
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
/**
|
|
||||||
* Function for getting hash from stringToHash
|
|
||||||
*
|
|
||||||
* @param stringToHash - stringToHash to encode
|
|
||||||
* @returns {string} - hash index
|
|
||||||
*/
|
|
||||||
module.exports = function getHashFromString(stringToHash) {
|
|
||||||
return stringToHash.split('').reduce((previousHashValue, currentChar) =>
|
|
||||||
(((previousHashValue << 5) - previousHashValue) + currentChar.charCodeAt(0)) | 0, 0);
|
|
||||||
};
|
|
78
src/utils/translation.js
Normal file
78
src/utils/translation.js
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
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'
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Function to translate string
|
||||||
|
*
|
||||||
|
* @param string - string to translate
|
||||||
|
* @returns {string} - translated string
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = function translateString(string) {
|
||||||
|
return string.replace(/[А-яёЁ]/g, (char) => translationTable[char] || char);
|
||||||
|
};
|
|
@ -8,7 +8,7 @@
|
||||||
<ul class="docs-aside__section-list">
|
<ul class="docs-aside__section-list">
|
||||||
{% for child in firstLevelPage.children %}
|
{% for child in firstLevelPage.children %}
|
||||||
<li>
|
<li>
|
||||||
<a href="/page/{{ child._id }}">
|
<a href="{{ child.uri }}">
|
||||||
{{ child.title }}
|
{{ child.title }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -1162,6 +1162,11 @@ bluebird@^3.5.1:
|
||||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7"
|
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7"
|
||||||
integrity sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==
|
integrity sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==
|
||||||
|
|
||||||
|
blueimp-md5@^2.10.0:
|
||||||
|
version "2.10.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.10.0.tgz#02f0843921f90dca14f5b8920a38593201d6964d"
|
||||||
|
integrity sha512-EkNUOi7tpV68TqjpiUz9D9NcT8um2+qtgntmMbi5UKssVX2m/2PLqotcric0RE63pB3HPN/fjf3cKHN2ufGSUQ==
|
||||||
|
|
||||||
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
|
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
|
||||||
version "4.11.8"
|
version "4.11.8"
|
||||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
|
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue