Comparing version 2.6.3 to 2.7.0
{ | ||
"name": "bse-admin", | ||
"version": "2.6.3", | ||
"version": "2.7.0", | ||
"repository": { | ||
@@ -5,0 +5,0 @@ "type": "git", |
@@ -129,4 +129,6 @@ var util = require('util'), | ||
if (!val) return; | ||
if (!val) { | ||
return; | ||
} | ||
var key = util.format('blocks:' + field + ':%s', sha(JSON.stringify(val))); | ||
@@ -133,0 +135,0 @@ |
@@ -1,3 +0,2 @@ | ||
var util = require('util'), | ||
_ = require('lodash'), | ||
var _ = require('lodash'), | ||
utility = require('../../util'), | ||
@@ -77,2 +76,1 @@ nodes = require('./index'), | ||
exports.ShowcaseNode = ShowcaseNode; | ||
@@ -12,3 +12,2 @@ 'use strict'; | ||
logger = require('../logger'), | ||
errors = require('../errors').TaskLibrariesFiles, | ||
storage = require('../providers/mds'); | ||
@@ -15,0 +14,0 @@ |
@@ -1,4 +0,4 @@ | ||
var util = require('util'), | ||
path = require('path'), | ||
url = require('url'), | ||
var Util = require('util'), | ||
Path = require('path'), | ||
Url = require('url'), | ||
@@ -11,600 +11,632 @@ _ = require('lodash'), | ||
logger = require('../logger'), | ||
REGEXP = { | ||
HREF: /<a\s+(?:[^>]*?\s+)?href="([^"]*)"/g, // <a href="..."> | ||
SRC: /<img\s+(?:[^>]*?\s+)?src="([^"]*)"/g, // <img src="..."> | ||
RELATIVE: { | ||
DOC: /^\.?\/?(?:\.\.\/)+?([\w|-]+)\.?[md|html|ru\.md|en\.md]?/, | ||
VERSION_DOC: /^\.?\/?(\d+\.\d+\.\d+)\-([\w|-]+)?\.?[md|html|ru\.md|en\.md]?/, | ||
BLOCK: /^\.\.?\/([\w|-]+)\/?([\w|-]+)?\.?[md|html|ru\.md|en\.md]?/, | ||
BLOCKS: /^\.?\/?(?:\.\.\/)?([\w|-]+)\.blocks\/([\w|-]+)\/?([\w|-]+)?\.[md|html|ru\.md|en\.md]/, | ||
LEVEL: /^\.\.?\/\.\.\/([\w|-]+)\.blocks\/([\w|-]+)\/?([\w|-]+)?\.?[md|html|ru\.md|en\.md]?/, | ||
BLOCK_FILES: /^\.?\/?(?:\.\.\/)?([\w|-]+)\.blocks\/([\w|-]+)\/?([\w|-]+)?\.(?![md|html|ru\.md|en\.md])/, | ||
JSON: /\w+\.json/ | ||
} | ||
}, | ||
PORTION_SIZE = 100; | ||
function buildHref(a) { | ||
return util.format('<a href="%s"', a); | ||
} | ||
module.exports = { | ||
replaceImageSources: function (content, node, doc) { | ||
var baseVersionGhUrl = node.ghLibVersionUrl + '/blob/' + node.route.conditions.version, | ||
buildReplace = function (str, src) { | ||
return str.replace(/src=("|')?.+("|')?/g, 'src="' + src + '?raw=true"'); | ||
}; | ||
return content.replace(/<img\s+(?:[^>]*?\s+)?src="([^"]*)"/g, function (str, src) { | ||
var imgUrl; | ||
/** | ||
* Check if link is e-mail link | ||
* @param {String} str link | ||
* @returns {boolean} | ||
*/ | ||
function isMailTo(str) { | ||
return /^mailto:/.test(str); | ||
} | ||
if (!src) { | ||
return str; | ||
} | ||
/** | ||
* Check if link is simple anchor link | ||
* @param {String} str link | ||
* @returns {boolean} | ||
*/ | ||
function isAnchor(str) { | ||
return /^#(.+)?/.test(str); | ||
} | ||
imgUrl = Url.parse(src); | ||
function isAbsolute(str) { | ||
return /^(https?:)?\/\//.test(str); | ||
} | ||
if (imgUrl.protocol && ['http:', 'https:'].indexOf(imgUrl.protocol) > -1) { | ||
return str; | ||
} | ||
/** | ||
* Check if link is simple anchor link | ||
* @param {String} str link | ||
* @param {Array} links that are exists in site model | ||
* @returns {boolean} | ||
*/ | ||
function isNativeSiteLink(str, links) { | ||
return links.indexOf(str.replace(/\/$/, '')) > -1; | ||
} | ||
//TODO ask for case with missed doc param | ||
return buildReplace(str, Url.resolve((doc ? doc.url : baseVersionGhUrl), src)); | ||
}); | ||
}, | ||
/** | ||
* Replace kind of broken links | ||
* @param {String} str link | ||
* @returns {String} | ||
*/ | ||
function fixBrokenLinkWithAmpersand(str) { | ||
return str.replace(/^&$/, ''); | ||
} | ||
/** | ||
* Returns true if give url has protocol and it is different then http or https | ||
* @param {Object} url - url parsed object | ||
* @returns {boolean} | ||
*/ | ||
hasUnsupportedProtocol: function (url) { | ||
return !!url.protocol && ['http:', 'https:'].indexOf(url.protocol) === -1; | ||
}, | ||
/** | ||
* Replace kind of broken links | ||
* @param {String} str link | ||
* @returns {String} | ||
*/ | ||
function fixBrokenLinkWithBracket(str) { | ||
return str.replace(/^\(/, ''); | ||
} | ||
/** | ||
* Returns true if given url has only hash (anchor) and does not have any other fields | ||
* @param {Object} url - url parsed object | ||
* @returns {boolean} | ||
*/ | ||
isAnchor: function (url) { | ||
return !!url.hash && !url.protocol && !url.host && !url.path; | ||
}, | ||
/** | ||
* Fix broken github links | ||
* @param {String} str link | ||
* @returns {*} | ||
*/ | ||
function fixGithubLinks(str) { | ||
str = (/^github\.com/.test(str) ? 'https://' : '') + str; | ||
return str.replace(/^\/\/github/, 'https://github'); | ||
} | ||
/** | ||
* Returns true if given url has protocol and it is one of http or https | ||
* @param {Object} url - url parsed object | ||
* @returns {boolean} | ||
*/ | ||
isAbsolute: function (url) { | ||
return !!url.protocol && ['http:', 'https:'].indexOf(url.protocol) > -1; | ||
}, | ||
/** | ||
* Try to build full github link by relative github link | ||
* @param {String} str link | ||
* @param {String} doc document | ||
* @param {String} treeOrBlob - 'tree' or 'blob' | ||
* @param {BaseNode} node | ||
* @returns {*} | ||
*/ | ||
/** | ||
* Returns true if hostname of given url has github word inside | ||
* @param {Object} url - url parsed object | ||
* @returns {boolean} | ||
*/ | ||
isGithub: function (url) { | ||
return url.hostname.indexOf('github') > -1; | ||
}, | ||
function buildFullGithubLinkForDocs(str, doc, treeOrBlob, node) { | ||
var jsonMatch = str.match(REGEXP.RELATIVE.JSON); | ||
/** | ||
* Make corrections on some broken hrefs | ||
* @param {String} href - link href attribute value | ||
* @returns {String} - fixed href | ||
*/ | ||
fixBroken: function (href) { | ||
href = href.replace(/^&$/, ''); | ||
href = href.replace(/^\(/, ''); | ||
href = (/^github\.com/.test(href) ? 'https://' : '') + href; | ||
return href.replace(/^\/\/github/, 'https://github'); | ||
}, | ||
if (doc && doc.repo) { | ||
/** | ||
* Returns true if path of given url is in the list of existed site urls | ||
* @param {Object} url - url parsed object | ||
* @param {String[]} existed - array of existed site urls | ||
* @returns {boolean} | ||
*/ | ||
isNativeSiteLink: function (url, existed) { | ||
return existed.indexOf(url.path.replace(/\/$/, '')) > -1; | ||
}, | ||
/** | ||
* Resolves given href on github url of document | ||
* @param {String} href - link href attribute value | ||
* @param {Object} doc - doc record value object. Has repo info inside | ||
* @param {String} treeOrBlob - special github url chunk. Can be 'tree' or 'blob' | ||
* @returns {String} - resolved url | ||
* @example | ||
* document url on github: 'https://github.com/user/repo/blob/master/foo/bar.md' | ||
* relative url in bar.md is: './bar1.md' or 'bar1.md' | ||
* resolved url will be: 'https://github.com/user/repo/blob/master/foo/bar1.md' | ||
*/ | ||
buildFullGhUrlToRelativeDocument: function (href, doc, treeOrBlob) { | ||
if (!doc || !doc.repo) { | ||
return href; | ||
} | ||
var repo = doc.repo, | ||
baseUrl = 'https://' + path.join(repo.host, repo.user, repo.repo, treeOrBlob, repo.ref, repo.path); | ||
baseUrl = 'https://' + Path.join(repo.host, repo.user, repo.repo, treeOrBlob, repo.ref, repo.path || ''); | ||
return Url.resolve(baseUrl, href); | ||
}, | ||
return url.resolve(baseUrl, str); | ||
} else if (jsonMatch) { | ||
/** | ||
* Builds full github links to json configuration files (package.json, bower.json, e.t.c) | ||
* @param {String} href - link href attribute value | ||
* @param {Object} node - page record value object. Contains route info inside. | ||
* @returns {String} - resolved url | ||
*/ | ||
buildFullGhUrlToJSONConfigurationFile: function (href, node) { | ||
var match = href.match(/(package\.json|bower\.json)/); | ||
if (!match) { | ||
return href; | ||
} | ||
var ghLibVersionUrl = node.ghLibVersionUrl, | ||
version = node.route.conditions.version; | ||
return [ghLibVersionUrl, 'blob', version, match[0]].join('/'); | ||
}, | ||
return [ghLibVersionUrl, treeOrBlob, version, jsonMatch[0]].join('/'); | ||
} | ||
/** | ||
* Builds full github links to non dociumentations files. For example to block source files | ||
* @param {String} href - link href attribute value | ||
* @param {Object} node - page record value object. Contains route info inside. | ||
* @returns {String} - resolved url | ||
*/ | ||
buildFullGHUrlToNonDocumentationFile: function (href, node) { | ||
var conditions = node.route.conditions, | ||
lib, version, match; | ||
return str; | ||
} | ||
if (!conditions || !conditions.lib || !conditions.version) { | ||
return href; | ||
} | ||
/** | ||
* Try to recognize different relative links for library embedded docs | ||
* @param {String} str link | ||
* @param {BaseNode} node | ||
* @returns {*} | ||
*/ | ||
function recognizeRelativeLinkForLibraryDocs(str, node) { | ||
var conditions, lib, version, match; | ||
lib = conditions.lib; | ||
version = conditions.version; | ||
conditions = node.route.conditions; | ||
if (!conditions) { | ||
return [str]; | ||
} | ||
match = href.match(/^\.?\/?(?:\.\.\/)?([\w|-]+)\.blocks\/([\w|-]+)\/?([\w|-]+)?\.(?![md|html|ru\.md|en\.md])/); | ||
if (match) { | ||
return [node.ghLibVersionUrl, 'blob', version, | ||
href.replace(/^\.?\/?(?:\.\.\/)?/, '').replace(/^\.?\/?/, '')].join('/'); | ||
} | ||
return href; | ||
}, | ||
lib = conditions.lib; | ||
version = conditions.version; | ||
/** | ||
* Builds site urls from relative links from one block to another on the same block level | ||
* @param {String} href attribute value | ||
* @param {Object} node - page record value object. Contains route info inside. | ||
* @returns {String} - resolved url | ||
* @example | ||
* Assuming that: | ||
* library name: my-lib | ||
* version: vx.y.z | ||
* level: desktop | ||
* block: input | ||
* | ||
* button.md => /libs/my-lib/vx.y.z/desktop/button | ||
* ./button.md => /libs/my-lib/vx.y.z/desktop/button | ||
* ../button/button.md => /libs/my-lib/vx.y.z/desktop/button | ||
*/ | ||
buildSiteUrlsForRelativeBlockLinksOnSameLevel: function (href, node) { | ||
var match = href.match(/^\.?\.?\/?([\w|-]+)\/?([\w|-]+)?\.?[md|html|ru\.md|en\.md]?/), | ||
conditions; | ||
if (!match) { | ||
return href; | ||
} | ||
if (!lib || !version) { | ||
return [str]; | ||
} | ||
conditions = node.route.conditions; | ||
return '/' + ['libs', conditions.lib, conditions.version, conditions.level, match[1]].join('/'); | ||
}, | ||
// common.blocks/button/button.ru.md | ||
match = str.match(REGEXP.RELATIVE.BLOCKS); | ||
if (match) { | ||
return ['desktop', 'touch-pad', 'touch-phone'].reduce(function (prev, item) { | ||
prev.push(util.format('/libs/%s/%s/%s/%s', lib, version, item, match[2])); | ||
return prev; | ||
}, []); | ||
} | ||
/** | ||
* Builds site urls from relative links from one block to another on different block levels | ||
* @param {String} href attribute value | ||
* @param {Object} node - page record value object. Contains route info inside. | ||
* @returns {String} - resolved url | ||
*/ | ||
buildSiteUrlsForRelativeBlockLinksOnDifferentLevel: function (href, node) { | ||
var match = href.match(/^\.\.?\/\.\.\/([\w|-]+)\.blocks\/([\w|-]+)\/?([\w|-]+)?\.?[md|html|ru\.md|en\.md]?/), | ||
conditions; | ||
if (!match) { | ||
return href; | ||
} | ||
// 3.1.0-changelog.md | ||
match = str.match(REGEXP.RELATIVE.VERSION_DOC); | ||
if (match) { | ||
return [util.format('/libs/%s/v%s/%s', lib, match[1], match[2])]; | ||
} | ||
conditions = node.route.conditions; | ||
return '/' + ['libs', conditions.lib, conditions.version, match[1], match[2]].join('/'); | ||
}, | ||
// ./changelog | ||
match = str.match(REGEXP.RELATIVE.DOC); | ||
if (match) { | ||
match[1] = match[1].toLowerCase(); | ||
return match[1] === 'readme' ? | ||
[util.format('/libs/%s/%s', lib, version)] : | ||
[util.format('/libs/%s/%s/%s', lib, version, match[1])]; | ||
} | ||
buildSiteUrlsForLibraryDocLinks: function (href, node) { | ||
var conditions = node.route.conditions, | ||
lib, version, match; | ||
// ../../popup/popup.js | ||
match = str.match(REGEXP.RELATIVE.BLOCK_FILES); | ||
if (match) { | ||
return node.ghLibVersionUrl + '/blob/' + version + str; | ||
} | ||
if (!conditions || !conditions.lib || !conditions.version) { | ||
return href; | ||
} | ||
return [str]; | ||
} | ||
lib = conditions.lib; | ||
version = conditions.version; | ||
/** | ||
* Try to build full github link to image by relative github link | ||
* @param {String} href link | ||
* @param {BaseNode} doc node | ||
* @returns {*} | ||
*/ | ||
function buildSrcForImages(str, href, doc, node) { | ||
var docParentUrl = doc && doc.url.replace(/\/\w*\.md/, '/'), // replace last url's part: repo/docs/a.md -> repo/docs | ||
absoluteLink = node.ghLibVersionUrl + '/blob/' + node.route.conditions.version, | ||
src = (doc ? docParentUrl : absoluteLink) + href; | ||
// common.blocks/button/button.ru.md | ||
match = href.match(/^\.?\/?(?:\.\.\/)?([\w|-]+)\.blocks\/([\w|-]+)\/?([\w|-]+)?\.[md|html|ru\.md|en\.md]/); | ||
if (match) { | ||
var level = match[1] === 'common' ? 'desktop' : match[1], | ||
block = match[2]; | ||
// change only src, save styles and attrs | ||
return str.replace(/src=("|')?.+("|')?/g, 'src="' + src + '?raw=true"') | ||
} | ||
return '/' + ['libs', lib, version, level, block].join('/'); | ||
} | ||
/** | ||
* Recognize links for blocks that are on the same level with current block | ||
* @param {String} str link | ||
* @param {BlockNode} node of library block | ||
* @returns {*} | ||
*/ | ||
function recognizeRelativeBlockLinkOnSameLevel(str, node) { | ||
var conditions = node.route.conditions, | ||
lib = conditions.lib, | ||
version = conditions.version, | ||
level = conditions.level, | ||
match = str.match(REGEXP.RELATIVE.BLOCK); | ||
// 3.1.0-changelog.md | ||
match = href.match(/^\.?\/?(\d+\.\d+\.\d+)\-([\w|-]+)?\.?[md|html|ru\.md|en\.md]?/); | ||
if (match) { | ||
return '/' + ['libs', lib, match[1], match[2]].join('/'); | ||
} | ||
if (match) { | ||
return util.format('/libs/%s/%s/%s/%s', lib, version, level, match[1]); | ||
} | ||
return str; | ||
} | ||
// ./changelog | ||
match = href.match(/^\.?\/?(?:\.\.\/)*?([\w|-]+)\.?[md|html|ru\.md|en\.md]?/); | ||
if (match) { | ||
match[1] = match[1].toLowerCase(); | ||
return '/' + (match[1] === 'readme' ? | ||
['libs', lib, version].join('/') : | ||
['libs', lib, version, match[1]].join('/')); | ||
} | ||
/** | ||
* Recognize links for blocks that are on different level from current block | ||
* @param {String} str link | ||
* @param {BlockNode} node of library block | ||
* @returns {*} | ||
*/ | ||
function recognizeRelativeBlockLinkOnDifferentLevels(str, node) { | ||
var conditions = node.route.conditions, | ||
lib = conditions.lib, | ||
version = conditions.version, | ||
match = str.match(REGEXP.RELATIVE.LEVEL); | ||
return href; | ||
}, | ||
if (match) { | ||
return util.format('/libs/%s/%s/%s/%s', lib, version, match[1], match[2]); | ||
} | ||
return str; | ||
} | ||
/** | ||
* Finds url for replacement from url hash and existedUrls | ||
* @param {String[]} variants | ||
* @param {Object} urlHash - gh -> site urls hash | ||
* @param {String[]} existedUrls - array of existed site urls | ||
* @returns {*} | ||
*/ | ||
findReplacement: function (variants, urlHash, existedUrls) { | ||
var replacement = null; | ||
/** | ||
* Override links for doc sources | ||
* @param {String} content doc node | ||
* @param {BaseNode} node - doc node | ||
* @param {Object} urlHash - hash with existed urls | ||
* @param {String} lang - language | ||
*/ | ||
function overrideLinks(content, node, urlHash, lang, doc) { | ||
if (!_.isString(content)) { | ||
return content; | ||
} | ||
variants.some(function (item) { | ||
if (urlHash[item]) { | ||
replacement = urlHash[item]; | ||
return true; | ||
} | ||
if (urlHash[item + '/README.md']) { | ||
replacement = urlHash[item + '/README.md']; | ||
return true; | ||
} | ||
if (existedUrls.indexOf(item) > -1) { | ||
replacement = item; | ||
return true; | ||
} | ||
return false; | ||
}); | ||
content = content.replace(REGEXP.SRC, function(str, href){ | ||
//if href is absolute (src="http://..." ) return href | ||
return /^http/.test(href) ? str : buildSrcForImages(str, href, doc, node); | ||
}); | ||
return replacement; | ||
}, | ||
content = content.replace(REGEXP.HREF, function (str, href) { | ||
// decode html entities | ||
href = href.replace(/&#(x?)([0-9a-fA-F]+);?/g, function (str, bs, match) { | ||
return String.fromCharCode(parseInt(match, bs === 'x' ? 16 : 10)); | ||
}); | ||
replaceLinkHrefs: function (content, node, doc, urlHash, existedUrls) { | ||
var _this = this; | ||
return content.replace(/<a\s+(?:[^>]*?\s+)?href="([^"]*)"/g, function (str, href) { | ||
var variants = [], | ||
replacement, | ||
originalHref = href, | ||
url; | ||
var existedLinks = _.values(urlHash); | ||
// nativeHref = href; | ||
// decode html entities | ||
href = href.replace(/&#(x?)([0-9a-fA-F]+);?/g, function (str, bs, match) { | ||
return String.fromCharCode(parseInt(match, bs === 'x' ? 16 : 10)); | ||
}); | ||
if (isMailTo(href) || isAnchor(href)) { | ||
return buildHref(href); | ||
} | ||
href = _this.fixBroken(href); | ||
url = Url.parse(href); | ||
href = fixBrokenLinkWithAmpersand(href); | ||
href = fixBrokenLinkWithBracket(href); | ||
href = fixGithubLinks(href); | ||
if (_this.isAnchor(url) || | ||
_this.hasUnsupportedProtocol(url) || | ||
_this.isNativeSiteLink(url, existedUrls) || | ||
(_this.isAbsolute(url) && !_this.isGithub(url))) { | ||
return str; | ||
} | ||
var hrefArr = href.split('#'), // extrude anchor from link | ||
anchor = hrefArr[1], | ||
links = [], | ||
replaced; | ||
href = Url.format(_.omit(url, 'hash')); // отрезаем якорь | ||
href = hrefArr[0]; | ||
if (_this.isAbsolute(url) && _this.isGithub(url)) { | ||
variants.push(href.replace(/\/tree\//, '/blob/')); | ||
variants.push(href.replace(/\/blob\//, '/tree/')); | ||
} else { | ||
variants.push(_this.buildFullGhUrlToRelativeDocument(href, doc, 'tree')); | ||
variants.push(_this.buildFullGhUrlToRelativeDocument(href, doc, 'blob')); | ||
if (isNativeSiteLink(href, existedLinks)) { | ||
return buildHref(href + (anchor ? '#' + anchor : '')); | ||
} | ||
variants.push(_this.buildSiteUrlsForLibraryDocLinks(href, node)); | ||
// try to recognize | ||
if (isAbsolute(href)) { | ||
links.push(href.replace(/\/tree\//, '/blob/')); | ||
links.push(href.replace(/\/blob\//, '/tree/')); | ||
} else { | ||
if(href.match(REGEXP.RELATIVE.JSON) || doc && doc.repo) { | ||
links.push(buildFullGithubLinkForDocs(href, doc, 'tree', node)); | ||
links.push(buildFullGithubLinkForDocs(href, doc, 'blob', node)); | ||
variants.push(_this.buildSiteUrlsForRelativeBlockLinksOnSameLevel(href, node)); | ||
variants.push(_this.buildSiteUrlsForRelativeBlockLinksOnDifferentLevel(href, node)); | ||
} | ||
links = links.concat(recognizeRelativeLinkForLibraryDocs(href, node)); | ||
if (node.source && node.source.data) { | ||
links.push(recognizeRelativeBlockLinkOnSameLevel(href, node)); | ||
} | ||
links.push(recognizeRelativeBlockLinkOnDifferentLevels(href, node)); | ||
} | ||
links.some(function (item) { | ||
if (urlHash[item]) { | ||
replaced = urlHash[item]; | ||
return true; | ||
replacement = _this.findReplacement(variants, urlHash, existedUrls); | ||
if (replacement) { | ||
href = replacement; | ||
} else if(_this.buildFullGhUrlToJSONConfigurationFile(href, node) !== href) { | ||
href = _this.buildFullGhUrlToJSONConfigurationFile(href, node); | ||
} else if (_this.buildFullGHUrlToNonDocumentationFile(href, node) !== href) { | ||
href = _this.buildFullGHUrlToNonDocumentationFile(href, node); | ||
} else if (!_this.isAbsolute(Url.parse(href))) { | ||
href = variants[0]; | ||
} | ||
if (urlHash[item + '/README.md']) { | ||
replaced = urlHash[item + '/README.md']; | ||
return true; | ||
if (url.hash) { | ||
href = Url.format(_.merge(Url.parse(href), { hash: url.hash })); | ||
} | ||
if (existedLinks.indexOf(item) > -1) { | ||
replaced = item; | ||
return true; | ||
/* | ||
if(originalHref !== href) { | ||
console.log('from: ' + $(this).attr('href') + ' to: ' + href + ' on page: ' + node.url); | ||
} | ||
return false; | ||
*/ | ||
return '<a href="' + href + '"'; | ||
}); | ||
}, | ||
if (replaced) { | ||
href = replaced; | ||
}else if (!isAbsolute(href)) { | ||
href = links[0]; | ||
/** | ||
* Override links for doc sources | ||
* @param {String} content doc node | ||
* @param {BaseNode} node - node record | ||
* @param {Object} doc - doc record | ||
* @param {Object} existed | ||
*/ | ||
overrideLinks: function(content, node, doc, existed) { | ||
if (!_.isString(content)) { | ||
return content; | ||
} | ||
href += (anchor ? '#' + anchor : ''); | ||
// if (nativeHref.match(/^.\//)) { | ||
// logger.debug(util.format('native: %s replaced: %s', nativeHref, href), module); | ||
// } | ||
return buildHref(href); | ||
}); | ||
// content = this.replaceImageSources(content, node, doc); | ||
content = this.replaceLinkHrefs(content, node, doc, existed.urlsHash, existed.existedUrls); | ||
return content; | ||
} | ||
return content; | ||
}, | ||
/** | ||
* Creates url hash for resolve links | ||
* @param {TargetBase} target object | ||
* @returns {Promise} | ||
*/ | ||
function collectUrls(target) { | ||
/** | ||
* Выбираем все записи документов (обычные посты и документация библиотек) | ||
* Фильтруем записи документов по критерию наличия url - адреса документа на github | ||
* и строим hash соответствия ключа записи - url | ||
* Загружаем все записи из пространства ключей NODE | ||
* строим итоговый хэш в котором значениями являются урлы страниц на сайте | ||
* а ключами урлы на гитхабе или id записей в случае блоков или отсутствия соответствий | ||
* url github -> site url | ||
* Creates url hash for resolve links | ||
* @param {TargetBase} target object | ||
* @returns {Promise} | ||
*/ | ||
return levelDb.get().getByKeyRange(target.KEY.DOCS_PREFIX, target.KEY.NODE_PREFIX) | ||
.then(function (docRecords) { | ||
return vow.all([ | ||
levelDb.get().getByKeyRange(target.KEY.NODE_PREFIX, target.KEY.PEOPLE_PREFIX), | ||
docRecords | ||
.filter(function (record) { | ||
return record.value.url; | ||
}) | ||
.reduce(function (prev, record) { | ||
prev[record.key] = record.value.url; | ||
collectUrls: function (target) { | ||
/** | ||
* Выбираем все записи документов (обычные посты и документация библиотек) | ||
* Фильтруем записи документов по критерию наличия url - адреса документа на github | ||
* и строим hash соответствия ключа записи - url | ||
* Загружаем все записи из пространства ключей NODE | ||
* строим итоговый хэш в котором значениями являются урлы страниц на сайте | ||
* а ключами урлы на гитхабе или id записей в случае блоков или отсутствия соответствий | ||
* url github -> site url | ||
*/ | ||
return levelDb.get().getByKeyRange(target.KEY.DOCS_PREFIX, target.KEY.NODE_PREFIX) | ||
.then(function (docRecords) { | ||
return vow.all([ | ||
levelDb.get().getByKeyRange(target.KEY.NODE_PREFIX, target.KEY.PEOPLE_PREFIX), | ||
docRecords | ||
.filter(function (record) { | ||
return record.value.url; | ||
}) | ||
.reduce(function (prev, record) { | ||
prev[record.key] = record.value.url; | ||
return prev; | ||
}, {}) | ||
]); | ||
}) | ||
.spread(function (nodeRecords, docUrlsHash) { | ||
var existedUrls = [], | ||
urlsHash = nodeRecords.reduce(function (prev, nodeRecord) { | ||
var nodeValue = nodeRecord.value; | ||
utility.getLanguages().forEach(function (lang) { | ||
if (!nodeValue.hidden[lang]) { | ||
var docUrl = docUrlsHash[Util.format('%s%s:%s', target.KEY.DOCS_PREFIX, nodeValue.id, lang)]; | ||
if (docUrl) { | ||
prev[docUrl] = nodeValue.url; | ||
} | ||
} | ||
}); | ||
existedUrls.push(nodeValue.url); | ||
return prev; | ||
}, {}) | ||
]); | ||
}) | ||
.spread(function (nodeRecords, docUrlsHash) { | ||
return nodeRecords.reduce(function (prev, nodeRecord) { | ||
var nodeValue = nodeRecord.value; | ||
}, {}); | ||
return { urlsHash: urlsHash, existedUrls: existedUrls }; | ||
}); | ||
}, | ||
utility.getLanguages().forEach(function (lang) { | ||
if (!nodeValue.hidden[lang]) { | ||
var docUrl = docUrlsHash[util.format('%s%s:%s', target.KEY.DOCS_PREFIX, nodeValue.id, lang)]; | ||
if (docUrl) { | ||
prev[docUrl] = nodeValue.url; | ||
} else { | ||
prev[nodeValue.id] = nodeValue.url; | ||
} | ||
} | ||
}); | ||
return prev; | ||
}, {}); | ||
}); | ||
} | ||
/** | ||
* Reads document page records from db | ||
* @param {TargetBase} target object | ||
* @param {Object[]} changedLibVersions - array with changed library versions | ||
* @returns {Promise} | ||
*/ | ||
function getDocumentRecordsFromDb (target, changedLibVersions) { | ||
/** | ||
* Здесь происходит выборка из пространства ключей NODE | ||
* по критериям: | ||
* | ||
* 1. view страницы должно быть 'post' | ||
* 2. Если это документ версии библиотеки, то версия библиотеки должна быть в модели измененных | ||
* Reads document page records from db | ||
* @param {TargetBase} target object | ||
* @param {Object[]} changedLibVersions - array with changed library versions | ||
* @returns {Promise} | ||
*/ | ||
return levelDb.get().getByCriteria(function (record) { | ||
var value = record.value, | ||
route = value.route, | ||
conditions, lib, version; | ||
getDocumentRecordsFromDb: function (target, changedLibVersions) { | ||
/** | ||
* Здесь происходит выборка из пространства ключей NODE | ||
* по критериям: | ||
* | ||
* 1. view страницы должно быть 'post' | ||
* 2. Если это документ версии библиотеки, то версия библиотеки должна быть в модели измененных | ||
*/ | ||
return levelDb.get().getByCriteria(function (record) { | ||
var value = record.value, | ||
route = value.route, | ||
conditions, lib, version; | ||
if (value.view !== 'post') { | ||
return false; | ||
} | ||
if (value.view !== 'post') { | ||
return false; | ||
} | ||
conditions = route.conditions; | ||
if (conditions && conditions.lib && conditions.version) { | ||
lib = conditions.lib; | ||
version = conditions.version; | ||
conditions = route.conditions; | ||
if (conditions && conditions.lib && conditions.version) { | ||
lib = conditions.lib; | ||
version = conditions.version; | ||
return changedLibVersions.some(function (item) { | ||
return item.lib === lib && item.version === version; | ||
}); | ||
} | ||
return changedLibVersions.some(function (item) { | ||
return item.lib === lib && item.version === version; | ||
}); | ||
} | ||
return true; | ||
}, { gte: target.KEY.NODE_PREFIX, lt: target.KEY.PEOPLE_PREFIX, fillCache: true }); | ||
} | ||
return true; | ||
}, { gte: target.KEY.NODE_PREFIX, lt: target.KEY.PEOPLE_PREFIX, fillCache: true }); | ||
}, | ||
/** | ||
* Reads block pages from database | ||
* @param {TargetBase} target object | ||
* @param {Object[]} changedLibVersions - array with changed library versions | ||
* @returns {Promise} | ||
*/ | ||
function getBlockRecordsFromDb(target, changedLibVersions) { | ||
/** | ||
* Здесь происходит выборка из пространства ключей NODE | ||
* по критериям: | ||
* | ||
* 1. view страницы должно быть 'block' | ||
* 2. Должен быть source.data - ссылка на запись с документацией блока | ||
* 3. Версия библиотеки должна быть в модели измененных | ||
*/ | ||
return levelDb.get().getByCriteria(function (record) { | ||
var value = record.value, | ||
route = value.route, | ||
conditions, lib, version; | ||
/** | ||
* Reads block pages from database | ||
* @param {TargetBase} target object | ||
* @param {Object[]} changedLibVersions - array with changed library versions | ||
* @returns {Promise} | ||
*/ | ||
getBlockRecordsFromDb: function (target, changedLibVersions) { | ||
/** | ||
* Здесь происходит выборка из пространства ключей NODE | ||
* по критериям: | ||
* | ||
* 1. view страницы должно быть 'block' | ||
* 2. Должен быть source.data - ссылка на запись с документацией блока | ||
* 3. Версия библиотеки должна быть в модели измененных | ||
*/ | ||
return levelDb.get().getByCriteria(function (record) { | ||
var value = record.value, | ||
route = value.route, | ||
conditions, lib, version; | ||
if (value.view !== 'block') { | ||
return false; | ||
} | ||
if (value.view !== 'block') { | ||
return false; | ||
} | ||
if (!value.source || !value.source.data) { | ||
return false; | ||
} | ||
if (!value.source || !value.source.data) { | ||
return false; | ||
} | ||
conditions = route.conditions; | ||
if (conditions && conditions.lib && conditions.version) { | ||
lib = conditions.lib; | ||
version = conditions.version; | ||
conditions = route.conditions; | ||
if (conditions && conditions.lib && conditions.version) { | ||
lib = conditions.lib; | ||
version = conditions.version; | ||
return changedLibVersions.some(function (item) { | ||
return item.lib === lib && item.version === version; | ||
}); | ||
} | ||
return changedLibVersions.some(function (item) { | ||
return item.lib === lib && item.version === version; | ||
}); | ||
} | ||
return false; | ||
}, { gte: target.KEY.NODE_PREFIX, lt: target.KEY.PEOPLE_PREFIX, fillCache: true }); | ||
} | ||
return false; | ||
}, { gte: target.KEY.NODE_PREFIX, lt: target.KEY.PEOPLE_PREFIX, fillCache: true }); | ||
}, | ||
/** | ||
* Overrides links for document page records | ||
* @param {TargetBase} target object | ||
* @param {Object} urlsHash - url comparison hash | ||
* @param {String []} languages - array of language identifiers | ||
* @param {Object[]} changedLibVersions - array with changed library versions | ||
* @returns {Promise} | ||
*/ | ||
function overrideLinksInDocuments (target, urlsHash, languages, changedLibVersions) { | ||
logger.debug('Start to override links in documents', module); | ||
/** | ||
* 1. Выбираем страницы документов из бд | ||
* 2. Делим массив полученных записей на массивы по 100 штук | ||
* 3. Последовательно выполняем переопределение ссылок для каждой порции записей | ||
* 3.1 Внутри порции переопредление ссылок для записей происходит параллельно | ||
* 3.2 Для каждой записи страницы выбираем связанную с ней запись документа | ||
* 3.3 Еслитаковая присутствует, то скармливаем ее content в переопределятор | ||
* 3.4 Сохраняем запись документа с измененным контентом | ||
* Overrides links for document page records | ||
* @param {TargetBase} target object | ||
* @param {Object} existed - exited urls data | ||
* @param {String []} languages - array of language identifiers | ||
* @param {Object[]} changedLibVersions - array with changed library versions | ||
* @returns {Promise} | ||
*/ | ||
return getDocumentRecordsFromDb(target, changedLibVersions).then(function (records) { | ||
logger.debug(util.format('Document records count: %s', records.length), module); | ||
var portionSize = PORTION_SIZE, | ||
portions = _.chunk(records, portionSize); | ||
overrideLinksInDocuments: function (target, existed, languages, changedLibVersions) { | ||
logger.debug('Start to override links in documents', module); | ||
var _this = this; | ||
/** | ||
* 1. Выбираем страницы документов из бд | ||
* 2. Делим массив полученных записей на массивы по 100 штук | ||
* 3. Последовательно выполняем переопределение ссылок для каждой порции записей | ||
* 3.1 Внутри порции переопредление ссылок для записей происходит параллельно | ||
* 3.2 Для каждой записи страницы выбираем связанную с ней запись документа | ||
* 3.3 Еслитаковая присутствует, то скармливаем ее content в переопределятор | ||
* 3.4 Сохраняем запись документа с измененным контентом | ||
*/ | ||
return this.getDocumentRecordsFromDb(target, changedLibVersions).then(function (records) { | ||
logger.debug(Util.format('Document records count: %s', records.length), module); | ||
var portionSize = PORTION_SIZE, | ||
portions = _.chunk(records, portionSize); | ||
logger.debug(util.format('Document records were divided into %s portions', portions.length), module); | ||
logger.debug(Util.format('Document records were divided into %s portions', portions.length), module); | ||
return portions.reduce(function (prev, item, index) { | ||
prev = prev.then(function () { | ||
logger.debug(util.format('override document links in range %s - %s', | ||
index * portionSize, (index + 1) * portionSize), module); | ||
return vow.allResolved(item.map(function (_item) { | ||
var nodeValue = _item.value; | ||
return vow.all(languages.map(function (lang) { | ||
var docKey = util.format('%s%s:%s', target.KEY.DOCS_PREFIX, nodeValue.id, lang); | ||
return levelDb.get().get(docKey) | ||
.then(function (docValue) { | ||
if (!docValue || !docValue.content) { | ||
return vow.resolve(); | ||
} | ||
docValue.content = overrideLinks(docValue.content, nodeValue, urlsHash, lang, docValue); | ||
return levelDb.get().put(docKey, docValue); | ||
}); | ||
})); | ||
})); | ||
}); | ||
return prev; | ||
}, vow.resolve()); | ||
}); | ||
} | ||
return portions.reduce(function (prev, item, index) { | ||
prev = prev.then(function () { | ||
logger.debug(Util.format('override document links in range %s - %s', | ||
index * portionSize, (index + 1) * portionSize), module); | ||
return vow.allResolved(item.map(function (_item) { | ||
var nodeValue = _item.value; | ||
return vow.all(languages.map(function (lang) { | ||
var docKey = Util.format('%s%s:%s', target.KEY.DOCS_PREFIX, nodeValue.id, lang); | ||
return levelDb.get().get(docKey) | ||
.then(function (docValue) { | ||
if (!docValue || !docValue.content) { | ||
return vow.resolve(); | ||
} | ||
docValue.content = _this.overrideLinks(docValue.content, nodeValue, docValue, existed); | ||
return levelDb.get().put(docKey, docValue); | ||
}, this); | ||
}, this)); | ||
}, this)); | ||
}, this); | ||
return prev; | ||
}.bind(this), vow.resolve()); | ||
}); | ||
}, | ||
/** | ||
* Overrides links for block page records | ||
* @param {TargetBase} target object | ||
* @param {Object} urlsHash - url comparison hash | ||
* @param {String []} languages - array of language identifiers | ||
* @param {Object[]} changedLibVersions - array with changed library versions | ||
* @returns {Promise} | ||
*/ | ||
function overrideLinksInBlocks(target, urlsHash, languages, changedLibVersions) { | ||
logger.debug('Start to override links in blocks', module); | ||
/** | ||
* 1. Выбираем страницы блоков из бд | ||
* 2. Делим массив полученных записей на массивы по 100 штук | ||
* 3. Последовательно выполняем переопределение ссылок для каждой порции записей | ||
* 3.1 Внутри порции переопредление ссылок для записей происходит параллельно | ||
* 3.2 Для каждой записи страницы выбираем связанную с ней запись докуметации блока | ||
* 3.3 Если таковая присутствует, то скармливаем ее content в переопределятор | ||
* с учетом различных форматов документации для разных библиотек и наличия нескольких языков | ||
* 3.4 Сохраняем запись документации блока с измененным контентом | ||
* Overrides links for block page records | ||
* @param {TargetBase} target object | ||
* @param {Object} existed - exited urls data | ||
* @param {String []} languages - array of language identifiers | ||
* @param {Object[]} changedLibVersions - array with changed library versions | ||
* @returns {Promise} | ||
*/ | ||
return getBlockRecordsFromDb(target, changedLibVersions).then(function (records) { | ||
logger.debug(util.format('Block records count: %s', records.length), module); | ||
var portionSize = PORTION_SIZE, | ||
portions = _.chunk(records, portionSize); | ||
overrideLinksInBlocks: function (target, existed, languages, changedLibVersions) { | ||
logger.debug('Start to override links in blocks', module); | ||
logger.debug(util.format('Block records were divided into %s portions', portions.length), module); | ||
var _this = this; | ||
/** | ||
* 1. Выбираем страницы блоков из бд | ||
* 2. Делим массив полученных записей на массивы по 100 штук | ||
* 3. Последовательно выполняем переопределение ссылок для каждой порции записей | ||
* 3.1 Внутри порции переопредление ссылок для записей происходит параллельно | ||
* 3.2 Для каждой записи страницы выбираем связанную с ней запись докуметации блока | ||
* 3.3 Если таковая присутствует, то скармливаем ее content в переопределятор | ||
* с учетом различных форматов документации для разных библиотек и наличия нескольких языков | ||
* 3.4 Сохраняем запись документации блока с измененным контентом | ||
*/ | ||
return this.getBlockRecordsFromDb(target, changedLibVersions).then(function (records) { | ||
logger.debug(Util.format('Block records count: %s', records.length), module); | ||
portionSize = PORTION_SIZE, | ||
portions = _.chunk(records, portionSize); | ||
return portions.reduce(function (prev, item, index) { | ||
prev = prev.then(function () { | ||
logger.debug(util.format('override block links in range %s - %s', | ||
index * portionSize, (index + 1) * portionSize), module); | ||
return vow.allResolved(item.map(function (_item) { | ||
var nodeValue = _item.value; | ||
logger.debug(Util.format('Block records were divided into %s portions', portions.length), module); | ||
return levelDb.get().get(nodeValue.source.data).then(function (blockValue) { | ||
if (!blockValue) { | ||
return vow.resolve(); | ||
} | ||
return portions.reduce(function (prev, item, index) { | ||
prev = prev.then(function () { | ||
logger.debug(Util.format('override block links in range %s - %s', | ||
index * portionSize, (index + 1) * portionSize), module); | ||
return vow.allResolved(item.map(function (_item) { | ||
var nodeValue = _item.value; | ||
languages.forEach(function (lang) { | ||
var description = blockValue[lang] ? blockValue[lang].description : blockValue.description; | ||
if (!description) { | ||
// logger.warn('there no description in block data for key %s', source.key); | ||
return; | ||
return levelDb.get().get(nodeValue.source.data).then(function (blockValue) { | ||
if (!blockValue) { | ||
return vow.resolve(); | ||
} | ||
if (_.isArray(description)) { | ||
// old bem-sets format | ||
description.forEach(function (item, index) { | ||
languages.forEach(function (lang) { | ||
var description = blockValue[lang] ? blockValue[lang].description : blockValue.description; | ||
if (!description) { | ||
// logger.warn('there no description in block data for key %s', source.key); | ||
return; | ||
} | ||
if (_.isArray(description)) { | ||
// old bem-sets format | ||
description.forEach(function (item, index) { | ||
if (blockValue[lang]) { | ||
blockValue[lang].description[index].content = | ||
_this.overrideLinks(item.content || '', nodeValue, null, existed); | ||
} else { | ||
blockValue.description[index].content = | ||
_this.overrideLinks(item.content || '', nodeValue, null, existed); | ||
} | ||
}); | ||
} else { | ||
if (blockValue[lang]) { | ||
blockValue[lang].description[index].content = | ||
overrideLinks(item.content || '', nodeValue, urlsHash, lang); | ||
blockValue[lang].description = | ||
_this.overrideLinks(description, nodeValue, null, existed); | ||
} else { | ||
blockValue.description[index].content = | ||
overrideLinks(item.content || '', nodeValue, urlsHash, lang); | ||
blockValue.description = | ||
_this.overrideLinks(description, nodeValue, null, existed); | ||
} | ||
}); | ||
} else { | ||
if (blockValue[lang]) { | ||
blockValue[lang].description = | ||
overrideLinks(description, nodeValue, urlsHash, lang); | ||
} else { | ||
blockValue.description = | ||
overrideLinks(description, nodeValue, urlsHash, lang); | ||
} | ||
} | ||
}); | ||
return levelDb.get().put(nodeValue.source.data, blockValue); | ||
}); | ||
})); | ||
}); | ||
return prev; | ||
}, vow.resolve()); | ||
}); | ||
}, | ||
return levelDb.get().put(nodeValue.source.data, blockValue); | ||
}); | ||
})); | ||
}); | ||
return prev; | ||
}, vow.resolve()); | ||
}); | ||
} | ||
run: function (target) { | ||
logger.info('Start overriding links in documents', module); | ||
module.exports = function (target) { | ||
logger.info('Start overriding links', module); | ||
if (!target.getChanges().areModified()) { | ||
logger.warn('No changes were made during this synchronization. This step will be skipped', module); | ||
return vow.resolve(target); | ||
} | ||
if (!target.getChanges().areModified()) { | ||
logger.warn('No changes were made during this synchronization. This step will be skipped', module); | ||
return vow.resolve(target); | ||
var languages = utility.getLanguages(), | ||
librariesChanges = target.getChanges().getLibraries(), | ||
existed, | ||
changedLibVersions = [] | ||
.concat(librariesChanges.getAdded()) | ||
.concat(librariesChanges.getModified()); | ||
return this.collectUrls(target) | ||
.then(function (_existed) { | ||
existed = _existed; | ||
logger.debug('Urls were collected. Start to process pages ...', module); | ||
return this.overrideLinksInDocuments(target, existed, languages, changedLibVersions); | ||
}, this) | ||
.then(function () { | ||
return this.overrideLinksInBlocks(target, existed, languages, changedLibVersions); | ||
}, this) | ||
.then(function () { | ||
logger.info('Links in documents were successfully overrided', module); | ||
return vow.resolve(target); | ||
}) | ||
.fail(function (err) { | ||
logger.error( | ||
Util.format('Overriding links failed with error %s', err.message), module); | ||
return vow.reject(err); | ||
}); | ||
} | ||
var languages = utility.getLanguages(), | ||
librariesChanges = target.getChanges().getLibraries(), | ||
changedLibVersions = [] | ||
.concat(librariesChanges.getAdded()) | ||
.concat(librariesChanges.getModified()), | ||
urlsHash; | ||
return collectUrls(target) | ||
.then(function (_urlsHash) { | ||
logger.debug('Urls were collected. Start to process pages ...', module); | ||
urlsHash = _urlsHash; | ||
return urlsHash; | ||
}) | ||
.then(function () { | ||
return overrideLinksInDocuments(target, urlsHash, languages, changedLibVersions); | ||
}) | ||
.then(function () { | ||
return overrideLinksInBlocks(target, urlsHash, languages, changedLibVersions); | ||
}) | ||
.then(function () { | ||
logger.info('Links were successfully overrided', module); | ||
return vow.resolve(target); | ||
}) | ||
.fail(function (err) { | ||
logger.error( | ||
util.format('Overriding links failed with error %s', err.message), module); | ||
return vow.reject(err); | ||
}); | ||
}; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
209444
4770