From f9a1951ec5039f03b94eb5ba749bdc312d73ff5b Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Fri, 13 Oct 2017 10:53:02 -0600 Subject: [PATCH 1/3] Enable running multiple upgrades at once --- app.js | 9 ++++----- src/upgrade.js | 18 +++++++----------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/app.js b/app.js index 686b355602..4ce28da224 100644 --- a/app.js +++ b/app.js @@ -219,14 +219,13 @@ function upgrade() { var meta = require('./src/meta'); var upgrade = require('./src/upgrade'); var build = require('./src/meta/build'); - var tasks = [db.init, meta.configs.init, upgrade.run, build.buildAll]; + var tasks = [db.init, meta.configs.init]; if (nconf.get('upgrade') !== true) { // Likely an upgrade script name passed in - tasks[2] = async.apply(upgrade.runSingle, nconf.get('upgrade')); - - // Skip build - tasks.pop(); + tasks.push(async.apply(upgrade.runParticular, nconf.get('upgrade').split(','))); + } else { + tasks.push(upgrade.run, build.buildAll); } // disable mongo timeouts during upgrade nconf.set('mongo:options:socketTimeoutMS', 0); diff --git a/src/upgrade.js b/src/upgrade.js index f1f5a016ad..97f5287bc6 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -87,23 +87,19 @@ Upgrade.run = function (callback) { }); }; -Upgrade.runSingle = function (query, callback) { +Upgrade.runParticular = function (names, callback) { process.stdout.write('\nParsing upgrade scripts... '); async.waterfall([ async.apply(file.walk, path.join(__dirname, './upgrades')), function (files, next) { - next(null, files.filter(function (file) { - return path.basename(file, '.js') === query; - })); - }, - ], function (err, files) { - if (err) { - return callback(err); - } + var upgrades = files.filter(function (file) { + return names.indexOf(path.basename(file, '.js')) !== -1; + }); - Upgrade.process(files, 0, callback); - }); + Upgrade.process(upgrades, 0, next); + }, + ], callback); }; Upgrade.process = function (files, skipCount, callback) { From 46fafb20b9079e68155db3e0f233a8df8003c47c Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Fri, 13 Oct 2017 21:02:41 -0600 Subject: [PATCH 2/3] Remove string.js dependency --- package.json | 1 - public/src/modules/helpers.js | 10 +- public/src/modules/string.js | 3 - public/src/modules/translator.js | 35 ++-- public/src/utils.js | 313 +++++++++++++++++++++++++++- src/controllers/accounts/profile.js | 4 +- src/controllers/topics.js | 3 +- src/flags.js | 3 +- src/messaging.js | 3 +- src/messaging/data.js | 3 +- src/meta/js.js | 1 - src/notifications.js | 5 +- src/posts/parse.js | 5 +- src/posts/summary.js | 4 +- src/socket.io/helpers.js | 6 +- src/socket.io/posts/edit.js | 4 +- src/topics/create.js | 4 +- src/topics/follow.js | 4 +- src/topics/teaser.js | 4 +- src/user/notifications.js | 4 +- src/user/profile.js | 3 +- test/build.js | 1 - test/utils.js | 22 ++ 23 files changed, 383 insertions(+), 62 deletions(-) delete mode 100644 public/src/modules/string.js diff --git a/package.json b/package.json index 89456f495d..2d1318e5d2 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,6 @@ "socket.io-redis": "5.2.0", "socketio-wildcard": "2.0.0", "spdx-license-list": "^3.0.1", - "string": "^3.3.3", "toobusy-js": "^0.5.1", "uglify-js": "^3.1.3", "validator": "9.0.0", diff --git a/public/src/modules/helpers.js b/public/src/modules/helpers.js index 736926101d..77a060ed7a 100644 --- a/public/src/modules/helpers.js +++ b/public/src/modules/helpers.js @@ -3,13 +3,13 @@ (function (factory) { if (typeof module === 'object' && module.exports) { var relative_path = require('nconf').get('relative_path'); - module.exports = factory(require('../utils'), require('benchpressjs'), require('string'), relative_path); + module.exports = factory(require('../utils'), require('benchpressjs'), relative_path); } else if (typeof define === 'function' && define.amd) { - define('helpers', ['benchpress', 'string'], function (Benchpress, string) { - return factory(utils, Benchpress, string, config.relative_path); + define('helpers', ['benchpress'], function (Benchpress) { + return factory(utils, Benchpress, config.relative_path); }); } -}(function (utils, Benchpress, S, relative_path) { +}(function (utils, Benchpress, relative_path) { var helpers = { displayMenuItem: displayMenuItem, buildMetaTag: buildMetaTag, @@ -92,7 +92,7 @@ } function stripTags(str) { - return S(String(str)).stripTags().s; + return utils.stripHTMLTags(str); } function generateCategoryBackground(category) { diff --git a/public/src/modules/string.js b/public/src/modules/string.js deleted file mode 100644 index a06e1862f9..0000000000 --- a/public/src/modules/string.js +++ /dev/null @@ -1,3 +0,0 @@ -/* -string.js - Copyright (C) 2012-2013, JP Richardson -*/!function(){"use strict";function n(e,t){t!==null&&t!==undefined?typeof t=="string"?e.s=t:e.s=t.toString():e.s=t,e.orig=t,t!==null&&t!==undefined?e.__defineGetter__?e.__defineGetter__("length",function(){return e.s.length}):e.length=t.length:e.length=-1}function r(e){n(this,e)}function u(){for(var e in s)(function(e){var t=s[e];i.hasOwnProperty(e)||(o.push(e),i[e]=function(){return String.prototype.s=this,t.apply(this,arguments)})})(e)}function a(){for(var e=0;er?n.slice(s,i):"")},camelize:function(){var e=this.trim().s.replace(/(\-|_|\s)+(.)?/g,function(e,t,n){return n?n.toUpperCase():""});return new this.constructor(e)},capitalize:function(){return new this.constructor(this.s.substr(0,1).toUpperCase()+this.s.substring(1).toLowerCase())},charAt:function(e){return this.s.charAt(e)},chompLeft:function(e){var t=this.s;return t.indexOf(e)===0?(t=t.slice(e.length),new this.constructor(t)):this},chompRight:function(e){if(this.endsWith(e)){var t=this.s;return t=t.slice(0,t.length-e.length),new this.constructor(t)}return this},collapseWhitespace:function(){var e=this.s.replace(/[\s\xa0]+/g," ").replace(/^\s+|\s+$/g,"");return new this.constructor(e)},contains:function(e){return this.s.indexOf(e)>=0},count:function(e){var t=0,n=this.s.indexOf(e);while(n>=0)t+=1,n=this.s.indexOf(e,n+1);return t},dasherize:function(){var e=this.trim().s.replace(/[_\s]+/g,"-").replace(/([A-Z])/g,"-$1").replace(/-+/g,"-").toLowerCase();return new this.constructor(e)},decodeHtmlEntities:function(){var e=this.s;return e=e.replace(/&#(\d+);?/g,function(e,t){return String.fromCharCode(t)}).replace(/&#[xX]([A-Fa-f0-9]+);?/g,function(e,t){return String.fromCharCode(parseInt(t,16))}).replace(/&([^;\W]+;?)/g,function(e,n){var r=n.replace(/;$/,""),i=t[n]||n.match(/;$/)&&t[r];return typeof i=="number"?String.fromCharCode(i):typeof i=="string"?i:e}),new this.constructor(e)},endsWith:function(e){var t=this.s.length-e.length;return t>=0&&this.s.indexOf(e,t)===t},escapeHTML:function(){return new this.constructor(this.s.replace(/[&<>"']/g,function(e){return"&"+m[e]+";"}))},ensureLeft:function(e){var t=this.s;return t.indexOf(e)===0?this:new this.constructor(e+t)},ensureRight:function(e){var t=this.s;return this.endsWith(e)?this:new this.constructor(t+e)},humanize:function(){if(this.s===null||this.s===undefined)return new this.constructor("");var e=this.underscore().replace(/_id$/,"").replace(/_/g," ").trim().capitalize();return new this.constructor(e)},isAlpha:function(){return!/[^a-z\xC0-\xFF]/.test(this.s.toLowerCase())},isAlphaNumeric:function(){return!/[^0-9a-z\xC0-\xFF]/.test(this.s.toLowerCase())},isEmpty:function(){return this.s===null||this.s===undefined?!0:/^[\s\xa0]*$/.test(this.s)},isLower:function(){return this.isAlpha()&&this.s.toLowerCase()===this.s},isNumeric:function(){return!/[^0-9]/.test(this.s)},isUpper:function(){return this.isAlpha()&&this.s.toUpperCase()===this.s},left:function(e){if(e>=0){var t=this.s.substr(0,e);return new this.constructor(t)}return this.right(-e)},lines:function(){return this.replaceAll("\r\n","\n").s.split("\n")},pad:function(e,t){t==null&&(t=" ");if(this.s.length>=e)return new this.constructor(this.s);e-=this.s.length;var n=Array(Math.ceil(e/2)+1).join(t),r=Array(Math.floor(e/2)+1).join(t);return new this.constructor(n+this.s+r)},padLeft:function(e,t){return t==null&&(t=" "),this.s.length>=e?new this.constructor(this.s):new this.constructor(Array(e-this.s.length+1).join(t)+this.s)},padRight:function(e,t){return t==null&&(t=" "),this.s.length>=e?new this.constructor(this.s):new this.constructor(this.s+Array(e-this.s.length+1).join(t))},parseCSV:function(e,t,n,r){e=e||",",n=n||"\\",typeof t=="undefined"&&(t='"');var i=0,s=[],o=[],u=this.s.length,a=!1,f=this,l=function(e){return f.s.charAt(e)};if(typeof r!="undefined")var c=[];t||(a=!0);while(i=0){var t=this.s.substr(this.s.length-e,e);return new this.constructor(t)}return this.left(-e)},setValue:function(e){return n(this,e),this},slugify:function(){var e=(new r(this.s.replace(/[^\w\s-]/g,"").toLowerCase())).dasherize().s;return e.charAt(0)==="-"&&(e=e.substr(1)),new this.constructor(e)},startsWith:function(e){return this.s.lastIndexOf(e,0)===0},stripPunctuation:function(){return new this.constructor(this.s.replace(/[^\w\s]|_/g,"").replace(/\s+/g," "))},stripTags:function(){var e=this.s,t=arguments.length>0?arguments:[""];return d(t,function(t){e=e.replace(RegExp("]*>","gi"),"")}),new this.constructor(e)},template:function(e,t,n){var r=this.s,t=t||p.TMPL_OPEN,n=n||p.TMPL_CLOSE,i=t.replace(/[-[\]()*\s]/g,"\\$&").replace(/\$/g,"\\$"),s=n.replace(/[-[\]()*\s]/g,"\\$&").replace(/\$/g,"\\$"),o=new RegExp(i+"(.+?)"+s,"g"),u=r.match(o)||[];return u.forEach(function(i){var s=i.substring(t.length,i.length-n.length);typeof e[s]!="undefined"&&(r=r.replace(i,e[s]))}),new this.constructor(r)},times:function(e){return new this.constructor((new Array(e+1)).join(this.s))},toBoolean:function(){if(typeof this.orig=="string"){var e=this.s.toLowerCase();return e==="true"||e==="yes"||e==="on"}return this.orig===!0||this.orig===1},toFloat:function(e){var t=parseFloat(this.s);return e?parseFloat(t.toFixed(e)):t},toInt:function(){return/^\s*-?0x/i.test(this.s)?parseInt(this.s,16):parseInt(this.s,10)},trim:function(){var e;return typeof i.trim=="undefined"?e=this.s.replace(/(^\s*|\s*$)/g,""):e=this.s.trim(),new this.constructor(e)},trimLeft:function(){var e;return i.trimLeft?e=this.s.trimLeft():e=this.s.replace(/(^\s*)/g,""),new this.constructor(e)},trimRight:function(){var e;return i.trimRight?e=this.s.trimRight():e=this.s.replace(/\s+$/,""),new this.constructor(e)},truncate:function(e,t){var n=this.s;e=~~e,t=t||"...";if(n.length<=e)return new this.constructor(n);var i=function(e){return e.toUpperCase()!==e.toLowerCase()?"A":" "},s=n.slice(0,e+1).replace(/.(?=\W*\w*$)/g,i);return s.slice(s.length-2).match(/\w\w/)?s=s.replace(/\s*\S+$/,""):s=(new r(s.slice(0,s.length-1))).trimRight().s,(s+t).length>n.length?new r(n):new r(n.slice(0,s.length)+t)},toCSV:function(){function u(e){return e!==null&&e!==""}var e=",",t='"',n="\\",i=!0,s=!1,o=[];typeof arguments[0]=="object"?(e=arguments[0].delimiter||e,e=arguments[0].separator||e,t=arguments[0].qualifier||t,i=!!arguments[0].encloseNumbers,n=arguments[0].escape||n,s=!!arguments[0].keys):typeof arguments[0]=="string"&&(e=arguments[0]),typeof arguments[1]=="string"&&(t=arguments[1]),arguments[1]===null&&(t=null);if(this.orig instanceof Array)o=this.orig;else for(var a in this.orig)this.orig.hasOwnProperty(a)&&(s?o.push(a):o.push(this.orig[a]));var f=n+t,l=[];for(var c=0;c",quot:'"',apos:"'",amp:"&"},m={};for(var g in v)m[v[g]]=g;t={amp:"&",gt:">",lt:"<",quot:'"',apos:"'",AElig:198,Aacute:193,Acirc:194,Agrave:192,Aring:197,Atilde:195,Auml:196,Ccedil:199,ETH:208,Eacute:201,Ecirc:202,Egrave:200,Euml:203,Iacute:205,Icirc:206,Igrave:204,Iuml:207,Ntilde:209,Oacute:211,Ocirc:212,Ograve:210,Oslash:216,Otilde:213,Ouml:214,THORN:222,Uacute:218,Ucirc:219,Ugrave:217,Uuml:220,Yacute:221,aacute:225,acirc:226,aelig:230,agrave:224,aring:229,atilde:227,auml:228,ccedil:231,eacute:233,ecirc:234,egrave:232,eth:240,euml:235,iacute:237,icirc:238,igrave:236,iuml:239,ntilde:241,oacute:243,ocirc:244,ograve:242,oslash:248,otilde:245,ouml:246,szlig:223,thorn:254,uacute:250,ucirc:251,ugrave:249,uuml:252,yacute:253,yuml:255,copy:169,reg:174,nbsp:160,iexcl:161,cent:162,pound:163,curren:164,yen:165,brvbar:166,sect:167,uml:168,ordf:170,laquo:171,not:172,shy:173,macr:175,deg:176,plusmn:177,sup1:185,sup2:178,sup3:179,acute:180,micro:181,para:182,middot:183,cedil:184,ordm:186,raquo:187,frac14:188,frac12:189,frac34:190,iquest:191,times:215,divide:247,"OElig;":338,"oelig;":339,"Scaron;":352,"scaron;":353,"Yuml;":376,"fnof;":402,"circ;":710,"tilde;":732,"Alpha;":913,"Beta;":914,"Gamma;":915,"Delta;":916,"Epsilon;":917,"Zeta;":918,"Eta;":919,"Theta;":920,"Iota;":921,"Kappa;":922,"Lambda;":923,"Mu;":924,"Nu;":925,"Xi;":926,"Omicron;":927,"Pi;":928,"Rho;":929,"Sigma;":931,"Tau;":932,"Upsilon;":933,"Phi;":934,"Chi;":935,"Psi;":936,"Omega;":937,"alpha;":945,"beta;":946,"gamma;":947,"delta;":948,"epsilon;":949,"zeta;":950,"eta;":951,"theta;":952,"iota;":953,"kappa;":954,"lambda;":955,"mu;":956,"nu;":957,"xi;":958,"omicron;":959,"pi;":960,"rho;":961,"sigmaf;":962,"sigma;":963,"tau;":964,"upsilon;":965,"phi;":966,"chi;":967,"psi;":968,"omega;":969,"thetasym;":977,"upsih;":978,"piv;":982,"ensp;":8194,"emsp;":8195,"thinsp;":8201,"zwnj;":8204,"zwj;":8205,"lrm;":8206,"rlm;":8207,"ndash;":8211,"mdash;":8212,"lsquo;":8216,"rsquo;":8217,"sbquo;":8218,"ldquo;":8220,"rdquo;":8221,"bdquo;":8222,"dagger;":8224,"Dagger;":8225,"bull;":8226,"hellip;":8230,"permil;":8240,"prime;":8242,"Prime;":8243,"lsaquo;":8249,"rsaquo;":8250,"oline;":8254,"frasl;":8260,"euro;":8364,"image;":8465,"weierp;":8472,"real;":8476,"trade;":8482,"alefsym;":8501,"larr;":8592,"uarr;":8593,"rarr;":8594,"darr;":8595,"harr;":8596,"crarr;":8629,"lArr;":8656,"uArr;":8657,"rArr;":8658,"dArr;":8659,"hArr;":8660,"forall;":8704,"part;":8706,"exist;":8707,"empty;":8709,"nabla;":8711,"isin;":8712,"notin;":8713,"ni;":8715,"prod;":8719,"sum;":8721,"minus;":8722,"lowast;":8727,"radic;":8730,"prop;":8733,"infin;":8734,"ang;":8736,"and;":8743,"or;":8744,"cap;":8745,"cup;":8746,"int;":8747,"there4;":8756,"sim;":8764,"cong;":8773,"asymp;":8776,"ne;":8800,"equiv;":8801,"le;":8804,"ge;":8805,"sub;":8834,"sup;":8835,"nsub;":8836,"sube;":8838,"supe;":8839,"oplus;":8853,"otimes;":8855,"perp;":8869,"sdot;":8901,"lceil;":8968,"rceil;":8969,"lfloor;":8970,"rfloor;":8971,"lang;":9001,"rang;":9002,"loz;":9674,"spades;":9824,"clubs;":9827,"hearts;":9829,"diams;":9830}}.call(this); diff --git a/public/src/modules/translator.js b/public/src/modules/translator.js index 465a240e4e..cd0d3c75c1 100644 --- a/public/src/modules/translator.js +++ b/public/src/modules/translator.js @@ -10,8 +10,8 @@ } if (typeof define === 'function' && define.amd) { // AMD. Register as a named module - define('translator', ['string'], function (string) { - return factory(string, loadClient, warn); + define('translator', [], function () { + return factory(utils, loadClient, warn); }); } else if (typeof module === 'object' && module.exports) { // Node @@ -37,15 +37,23 @@ }); } - module.exports = factory(require('string'), loadServer, warn); + module.exports = factory(require('../utils'), loadServer, warn); }()); - } else { - window.translator = factory(window.string, loadClient, warn); } -}(function (string, load, warn) { +}(function (utils, load, warn) { var assign = Object.assign || jQuery.extend; function classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + function escapeHTML(str) { + return utils.decodeHTMLEntities( + String(str) + .replace(/[\s\xa0]+/g, ' ') + .replace(/^\s+|\s+$/g, '') + ).replace(/[<>]/g, function (c) { + return c === '<' ? '<' : '>'; + }); + } + var Translator = (function () { /** * Construct a new Translator object @@ -284,9 +292,7 @@ } var argsToTranslate = args.map(function (arg) { - return string(arg).collapseWhitespace().decodeHTMLEntities().escapeHTML().s.replace(/&/g, '&'); - }).map(function (arg) { - return self.translate(arg); + return self.translate(escapeHTML(arg)); }); return Promise.all(argsToTranslate).then(function (translatedArgs) { @@ -539,12 +545,13 @@ return cb(''); } - Translator.create(lang).translate(text).catch(function (err) { + return Translator.create(lang).translate(text).then(function (output) { + if (cb) { + setTimeout(cb, 0, output); + } + return output; + }, function (err) { warn('Translation failed: ' + err.stack); - }).then(function (output) { - cb(output); - }).catch(function (err) { - console.error(err); }); }, diff --git a/public/src/utils.js b/public/src/utils.js index 910082a430..328c73ebe0 100644 --- a/public/src/utils.js +++ b/public/src/utils.js @@ -25,6 +25,279 @@ window.utils = factory(window.XRegExp); } }(function (XRegExp) { + var freeze = Object.freeze || function (obj) { return obj; }; + + // add default escape function for escaping HTML entities + var escapeCharMap = freeze({ + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '`': '`', + '=': '=', + }); + function replaceChar(c) { + return escapeCharMap[c]; + } + var escapeChars = /[&<>"'`=]/g; + + var HTMLEntities = freeze({ + amp: '&', + gt: '>', + lt: '<', + quot: '"', + apos: "'", + AElig: 198, + Aacute: 193, + Acirc: 194, + Agrave: 192, + Aring: 197, + Atilde: 195, + Auml: 196, + Ccedil: 199, + ETH: 208, + Eacute: 201, + Ecirc: 202, + Egrave: 200, + Euml: 203, + Iacute: 205, + Icirc: 206, + Igrave: 204, + Iuml: 207, + Ntilde: 209, + Oacute: 211, + Ocirc: 212, + Ograve: 210, + Oslash: 216, + Otilde: 213, + Ouml: 214, + THORN: 222, + Uacute: 218, + Ucirc: 219, + Ugrave: 217, + Uuml: 220, + Yacute: 221, + aacute: 225, + acirc: 226, + aelig: 230, + agrave: 224, + aring: 229, + atilde: 227, + auml: 228, + ccedil: 231, + eacute: 233, + ecirc: 234, + egrave: 232, + eth: 240, + euml: 235, + iacute: 237, + icirc: 238, + igrave: 236, + iuml: 239, + ntilde: 241, + oacute: 243, + ocirc: 244, + ograve: 242, + oslash: 248, + otilde: 245, + ouml: 246, + szlig: 223, + thorn: 254, + uacute: 250, + ucirc: 251, + ugrave: 249, + uuml: 252, + yacute: 253, + yuml: 255, + copy: 169, + reg: 174, + nbsp: 160, + iexcl: 161, + cent: 162, + pound: 163, + curren: 164, + yen: 165, + brvbar: 166, + sect: 167, + uml: 168, + ordf: 170, + laquo: 171, + not: 172, + shy: 173, + macr: 175, + deg: 176, + plusmn: 177, + sup1: 185, + sup2: 178, + sup3: 179, + acute: 180, + micro: 181, + para: 182, + middot: 183, + cedil: 184, + ordm: 186, + raquo: 187, + frac14: 188, + frac12: 189, + frac34: 190, + iquest: 191, + times: 215, + divide: 247, + 'OElig;': 338, + 'oelig;': 339, + 'Scaron;': 352, + 'scaron;': 353, + 'Yuml;': 376, + 'fnof;': 402, + 'circ;': 710, + 'tilde;': 732, + 'Alpha;': 913, + 'Beta;': 914, + 'Gamma;': 915, + 'Delta;': 916, + 'Epsilon;': 917, + 'Zeta;': 918, + 'Eta;': 919, + 'Theta;': 920, + 'Iota;': 921, + 'Kappa;': 922, + 'Lambda;': 923, + 'Mu;': 924, + 'Nu;': 925, + 'Xi;': 926, + 'Omicron;': 927, + 'Pi;': 928, + 'Rho;': 929, + 'Sigma;': 931, + 'Tau;': 932, + 'Upsilon;': 933, + 'Phi;': 934, + 'Chi;': 935, + 'Psi;': 936, + 'Omega;': 937, + 'alpha;': 945, + 'beta;': 946, + 'gamma;': 947, + 'delta;': 948, + 'epsilon;': 949, + 'zeta;': 950, + 'eta;': 951, + 'theta;': 952, + 'iota;': 953, + 'kappa;': 954, + 'lambda;': 955, + 'mu;': 956, + 'nu;': 957, + 'xi;': 958, + 'omicron;': 959, + 'pi;': 960, + 'rho;': 961, + 'sigmaf;': 962, + 'sigma;': 963, + 'tau;': 964, + 'upsilon;': 965, + 'phi;': 966, + 'chi;': 967, + 'psi;': 968, + 'omega;': 969, + 'thetasym;': 977, + 'upsih;': 978, + 'piv;': 982, + 'ensp;': 8194, + 'emsp;': 8195, + 'thinsp;': 8201, + 'zwnj;': 8204, + 'zwj;': 8205, + 'lrm;': 8206, + 'rlm;': 8207, + 'ndash;': 8211, + 'mdash;': 8212, + 'lsquo;': 8216, + 'rsquo;': 8217, + 'sbquo;': 8218, + 'ldquo;': 8220, + 'rdquo;': 8221, + 'bdquo;': 8222, + 'dagger;': 8224, + 'Dagger;': 8225, + 'bull;': 8226, + 'hellip;': 8230, + 'permil;': 8240, + 'prime;': 8242, + 'Prime;': 8243, + 'lsaquo;': 8249, + 'rsaquo;': 8250, + 'oline;': 8254, + 'frasl;': 8260, + 'euro;': 8364, + 'image;': 8465, + 'weierp;': 8472, + 'real;': 8476, + 'trade;': 8482, + 'alefsym;': 8501, + 'larr;': 8592, + 'uarr;': 8593, + 'rarr;': 8594, + 'darr;': 8595, + 'harr;': 8596, + 'crarr;': 8629, + 'lArr;': 8656, + 'uArr;': 8657, + 'rArr;': 8658, + 'dArr;': 8659, + 'hArr;': 8660, + 'forall;': 8704, + 'part;': 8706, + 'exist;': 8707, + 'empty;': 8709, + 'nabla;': 8711, + 'isin;': 8712, + 'notin;': 8713, + 'ni;': 8715, + 'prod;': 8719, + 'sum;': 8721, + 'minus;': 8722, + 'lowast;': 8727, + 'radic;': 8730, + 'prop;': 8733, + 'infin;': 8734, + 'ang;': 8736, + 'and;': 8743, + 'or;': 8744, + 'cap;': 8745, + 'cup;': 8746, + 'int;': 8747, + 'there4;': 8756, + 'sim;': 8764, + 'cong;': 8773, + 'asymp;': 8776, + 'ne;': 8800, + 'equiv;': 8801, + 'le;': 8804, + 'ge;': 8805, + 'sub;': 8834, + 'sup;': 8835, + 'nsub;': 8836, + 'sube;': 8838, + 'supe;': 8839, + 'oplus;': 8853, + 'otimes;': 8855, + 'perp;': 8869, + 'sdot;': 8901, + 'lceil;': 8968, + 'rceil;': 8969, + 'lfloor;': 8970, + 'rfloor;': 8971, + 'lang;': 9001, + 'rang;': 9002, + 'loz;': 9674, + 'spades;': 9824, + 'clubs;': 9827, + 'hearts;': 9829, + 'diams;': 9830, + }); + var utils = { generateUUID: function () { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { @@ -33,6 +306,35 @@ return v.toString(16); }); }, + // https://github.com/substack/node-ent/blob/master/index.js + decodeHTMLEntities: function (html) { + return String(html) + .replace(/&#(\d+);?/g, function (_, code) { + return String.fromCharCode(code); + }) + .replace(/&#[xX]([A-Fa-f0-9]+);?/g, function (_, hex) { + return String.fromCharCode(parseInt(hex, 16)); + }) + .replace(/&([^;\W]+;?)/g, function (m, e) { + var ee = e.replace(/;$/, ''); + var target = HTMLEntities[e] || (e.match(/;$/) && HTMLEntities[ee]); + + if (typeof target === 'number') { + return String.fromCharCode(target); + } else if (typeof target === 'string') { + return target; + } + + return m; + }); + }, + // https://github.com/jprichardson/string.js/blob/master/lib/string.js + stripHTMLTags: function (str, tags) { + var pattern = (tags || ['']).map(function (tag) { + return utils.escapeRegexChars(tag); + }).join('|'); + return String(str).replace(new RegExp(']*>', 'gi'), ''); + }, invalidUnicodeChars: XRegExp('[^\\p{L}\\s\\d\\-_]', 'g'), invalidLatinChars: /[^\w\s\d\-_]/g, @@ -232,8 +534,15 @@ return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); }, - escapeHTML: function (raw) { - return raw.replace(/&/gm, '&').replace(//gm, '>'); + escapeHTML: function (str) { + if (str == null) { + return ''; + } + if (!str) { + return String(str); + } + + return str.toString().replace(escapeChars, replaceChar); }, isAndroidBrowser: function () { diff --git a/src/controllers/accounts/profile.js b/src/controllers/accounts/profile.js index e067ed3ef5..fc9f9cb5b4 100644 --- a/src/controllers/accounts/profile.js +++ b/src/controllers/accounts/profile.js @@ -2,7 +2,6 @@ var nconf = require('nconf'); var async = require('async'); -var S = require('string'); var user = require('../../user'); var posts = require('../../posts'); @@ -13,6 +12,7 @@ var helpers = require('../helpers'); var pagination = require('../../pagination'); var messaging = require('../../messaging'); var translator = require('../../translator'); +var utils = require('../../utils'); var profileController = module.exports; @@ -87,7 +87,7 @@ profileController.get = function (req, res, callback) { userData.profileviews = 1; } - var plainAboutMe = userData.aboutme ? S(userData.aboutme).decodeHTMLEntities().stripTags().s : ''; + var plainAboutMe = userData.aboutme ? utils.stripHTMLTags(utils.decodeHTMLEntities(userData.aboutme)) : ''; res.locals.metaTags = [ { diff --git a/src/controllers/topics.js b/src/controllers/topics.js index d3a4846390..c75f5c3602 100644 --- a/src/controllers/topics.js +++ b/src/controllers/topics.js @@ -2,7 +2,6 @@ var async = require('async'); -var S = require('string'); var nconf = require('nconf'); var user = require('../user'); @@ -217,7 +216,7 @@ function addTags(topicData, req, res) { var postAtIndex = findPost(Math.max(0, req.params.post_index - 1)); if (postAtIndex && postAtIndex.content) { - description = S(postAtIndex.content).decodeHTMLEntities().stripTags().s; + description = utils.stripHTMLTags(utils.decodeHTMLEntities(postAtIndex.content)); } if (description.length > 255) { diff --git a/src/flags.js b/src/flags.js index 749878db33..a578accf18 100644 --- a/src/flags.js +++ b/src/flags.js @@ -2,7 +2,6 @@ var async = require('async'); var _ = require('lodash'); -var S = require('string'); var winston = require('winston'); var validator = require('validator'); @@ -660,7 +659,7 @@ Flags.notify = function (flagObj, uid, callback) { return callback(err); } - var title = S(results.title).decodeHTMLEntities().s; + var title = utils.decodeHTMLEntities(results.title); var titleEscaped = title.replace(/%/g, '%').replace(/,/g, ','); notifications.create({ diff --git a/src/messaging.js b/src/messaging.js index 9a53f327ce..a90c36c2c5 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -2,7 +2,6 @@ var async = require('async'); -var S = require('string'); var validator = require('validator'); var db = require('./database'); @@ -215,7 +214,7 @@ Messaging.getTeaser = function (uid, roomId, callback) { return callback(); } if (teaser.content) { - teaser.content = S(teaser.content).stripTags().decodeHTMLEntities().s; + teaser.content = utils.stripHTMLTags(utils.decodeHTMLEntities(teaser.content)); teaser.content = validator.escape(String(teaser.content)); } diff --git a/src/messaging/data.js b/src/messaging/data.js index b3a2ba58dd..88b6683a11 100644 --- a/src/messaging/data.js +++ b/src/messaging/data.js @@ -1,7 +1,6 @@ 'use strict'; var async = require('async'); -var S = require('string'); var db = require('../database'); var user = require('../user'); @@ -73,7 +72,7 @@ module.exports = function (Messaging) { return next(err); } message.content = result; - message.cleanedContent = S(result).stripTags().decodeHTMLEntities().s; + message.cleanedContent = utils.stripHTMLTags(utils.decodeHTMLEntities(result)); next(null, message); }); }, next); diff --git a/src/meta/js.js b/src/meta/js.js index 8082be2412..28c5255db0 100644 --- a/src/meta/js.js +++ b/src/meta/js.js @@ -72,7 +72,6 @@ JS.scripts = { 'public/src/modules/alerts.js', 'public/src/modules/taskbar.js', 'public/src/modules/helpers.js', - 'public/src/modules/string.js', 'public/src/modules/flags.js', 'public/src/modules/storage.js', ], diff --git a/src/notifications.js b/src/notifications.js index 098efe5d9f..919d5a22e4 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -4,7 +4,6 @@ var async = require('async'); var winston = require('winston'); var cron = require('cron').CronJob; var nconf = require('nconf'); -var S = require('string'); var _ = require('lodash'); var db = require('./database'); @@ -56,7 +55,7 @@ Notifications.getMultiple = function (nids, callback) { notification.datetimeISO = utils.toISOString(notification.datetime); if (notification.bodyLong) { - notification.bodyLong = S(notification.bodyLong).escapeHTML().s; + notification.bodyLong = utils.escapeHTML(notification.bodyLong); } notification.user = usersData[index]; @@ -470,7 +469,7 @@ Notifications.merge = function (notifications, callback) { }); var numUsers = usernames.length; - var title = S(notifications[modifyIndex].topicTitle || '').decodeHTMLEntities().s; + var title = utils.decodeHTMLEntities(notifications[modifyIndex].topicTitle || ''); var titleEscaped = title.replace(/%/g, '%').replace(/,/g, ','); titleEscaped = titleEscaped ? (', ' + titleEscaped) : ''; diff --git a/src/posts/parse.js b/src/posts/parse.js index d46eaa800d..d6f7165f19 100644 --- a/src/posts/parse.js +++ b/src/posts/parse.js @@ -4,12 +4,12 @@ var async = require('async'); var nconf = require('nconf'); var url = require('url'); var winston = require('winston'); -var S = require('string'); var meta = require('../meta'); var cache = require('./cache'); var plugins = require('../plugins'); var translator = require('../translator'); +var utils = require('../utils'); module.exports = function (Posts) { Posts.urlRegex = { @@ -82,7 +82,6 @@ module.exports = function (Posts) { function sanitizeSignature(signature) { signature = translator.escape(signature); - var string = S(signature); var tagsToStrip = []; if (parseInt(meta.config['signatures:disableLinks'], 10) === 1) { @@ -93,6 +92,6 @@ module.exports = function (Posts) { tagsToStrip.push('img'); } - return tagsToStrip.length ? string.stripTags.apply(string, tagsToStrip).s : signature; + return utils.stripHTMLTags(signature, tagsToStrip); } }; diff --git a/src/posts/summary.js b/src/posts/summary.js index 6c1b6d1958..902a003445 100644 --- a/src/posts/summary.js +++ b/src/posts/summary.js @@ -3,7 +3,6 @@ var async = require('async'); var validator = require('validator'); -var S = require('string'); var _ = require('lodash'); var topics = require('../topics'); @@ -144,8 +143,7 @@ module.exports = function (Posts) { function stripTags(content) { if (content) { - var s = S(content); - return s.stripTags.apply(s, utils.stripTags).s; + return utils.stripHTMLTags(content, utils.stripTags); } return content; } diff --git a/src/socket.io/helpers.js b/src/socket.io/helpers.js index 0f158cd080..cbc01aff60 100644 --- a/src/socket.io/helpers.js +++ b/src/socket.io/helpers.js @@ -2,7 +2,6 @@ var async = require('async'); var winston = require('winston'); -var S = require('string'); var db = require('../database'); var websockets = require('./index'); @@ -12,6 +11,7 @@ var topics = require('../topics'); var privileges = require('../privileges'); var notifications = require('../notifications'); var plugins = require('../plugins'); +var utils = require('../utils'); var SocketHelpers = {}; @@ -105,7 +105,7 @@ SocketHelpers.sendNotificationToPostOwner = function (pid, fromuid, command, not }, next); }, function (results, next) { - var title = S(results.topicTitle).decodeHTMLEntities().s; + var title = utils.decodeHTMLEntities(results.topicTitle); var titleEscaped = title.replace(/%/g, '%').replace(/,/g, ','); notifications.create({ @@ -151,7 +151,7 @@ SocketHelpers.sendNotificationToTopicOwner = function (tid, fromuid, command, no return; } ownerUid = results.topicData.uid; - var title = S(results.topicData.title).decodeHTMLEntities().s; + var title = utils.decodeHTMLEntities(results.topicData.title); var titleEscaped = title.replace(/%/g, '%').replace(/,/g, ','); notifications.create({ diff --git a/src/socket.io/posts/edit.js b/src/socket.io/posts/edit.js index 00ae540496..48f750c2ce 100644 --- a/src/socket.io/posts/edit.js +++ b/src/socket.io/posts/edit.js @@ -3,12 +3,12 @@ var async = require('async'); var validator = require('validator'); var _ = require('lodash'); -var S = require('string'); var posts = require('../../posts'); var groups = require('../../groups'); var events = require('../../events'); var meta = require('../../meta'); +var utils = require('../../utils'); var websockets = require('../index'); module.exports = function (SocketPosts) { @@ -20,7 +20,7 @@ module.exports = function (SocketPosts) { } // Trim and remove HTML (latter for composers that send in HTML, like redactor) - var contentLen = S(data.content).stripTags().s.trim().length; + var contentLen = utils.stripHTMLTags(data.content).trim().length; if (data.title && data.title.length < parseInt(meta.config.minimumTitleLength, 10)) { return callback(new Error('[[error:title-too-short, ' + meta.config.minimumTitleLength + ']]')); diff --git a/src/topics/create.js b/src/topics/create.js index f5a51e7e77..edb1f1b84d 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -4,7 +4,7 @@ var async = require('async'); var _ = require('lodash'); var validator = require('validator'); -var S = require('string'); + var db = require('../database'); var utils = require('../utils'); var plugins = require('../plugins'); @@ -343,7 +343,7 @@ module.exports = function (Topics) { function check(item, min, max, minError, maxError, callback) { // Trim and remove HTML (latter for composers that send in HTML, like redactor) if (typeof item === 'string') { - item = S(item).stripTags().s.trim(); + item = utils.stripHTMLTags(item).trim(); } if (item === null || item === undefined || item.length < parseInt(min, 10)) { diff --git a/src/topics/follow.js b/src/topics/follow.js index f1981766a2..f1bad3ccf3 100644 --- a/src/topics/follow.js +++ b/src/topics/follow.js @@ -2,7 +2,6 @@ 'use strict'; var async = require('async'); -var S = require('string'); var winston = require('winston'); var db = require('../database'); @@ -13,6 +12,7 @@ var privileges = require('../privileges'); var meta = require('../meta'); var emailer = require('../emailer'); var plugins = require('../plugins'); +var utils = require('../utils'); module.exports = function (Topics) { Topics.toggleFollow = function (tid, uid, callback) { @@ -214,7 +214,7 @@ module.exports = function (Topics) { title = postData.topic.title; if (title) { - title = S(title).decodeHTMLEntities().s; + title = utils.decodeHTMLEntities(title); titleEscaped = title.replace(/%/g, '%').replace(/,/g, ','); } diff --git a/src/topics/teaser.js b/src/topics/teaser.js index 09d4d048ae..2827e74d41 100644 --- a/src/topics/teaser.js +++ b/src/topics/teaser.js @@ -3,7 +3,6 @@ var async = require('async'); var _ = require('lodash'); -var S = require('string'); var winston = require('winston'); var meta = require('../meta'); @@ -91,8 +90,7 @@ module.exports = function (Topics) { if (tidToPost[topic.tid]) { tidToPost[topic.tid].index = meta.config.teaserPost === 'first' ? 1 : counts[index]; if (tidToPost[topic.tid].content) { - var s = S(tidToPost[topic.tid].content); - tidToPost[topic.tid].content = s.stripTags.apply(s, utils.stripTags).s; + tidToPost[topic.tid].content = utils.stripHTMLTags(tidToPost[topic.tid].content, utils.stripTags); } } return tidToPost[topic.tid]; diff --git a/src/user/notifications.js b/src/user/notifications.js index 4e2dcba7e8..a446a9a822 100644 --- a/src/user/notifications.js +++ b/src/user/notifications.js @@ -3,12 +3,12 @@ var async = require('async'); var winston = require('winston'); -var S = require('string'); var db = require('../database'); var meta = require('../meta'); var notifications = require('../notifications'); var privileges = require('../privileges'); +var utils = require('../utils'); var UserNotifications = module.exports; @@ -281,7 +281,7 @@ UserNotifications.sendTopicNotificationToFollowers = function (uid, topicData, p var title = topicData.title; if (title) { - title = S(title).decodeHTMLEntities().s; + title = utils.decodeHTMLEntities(title); } notifications.create({ diff --git a/src/user/profile.js b/src/user/profile.js index dbdcbcba85..70dffeebe6 100644 --- a/src/user/profile.js +++ b/src/user/profile.js @@ -2,7 +2,6 @@ 'use strict'; var async = require('async'); -var S = require('string'); var utils = require('../utils'); var meta = require('../meta'); @@ -61,7 +60,7 @@ module.exports = function (User) { } else if (field === 'fullname') { return updateFullname(updateUid, data.fullname, next); } else if (field === 'signature') { - data[field] = S(data[field]).stripTags().s; + data[field] = utils.stripHTMLTags(data[field]); } User.setUserField(updateUid, field, data[field], next); diff --git a/test/build.js b/test/build.js index 1c346ad621..35471d0fd6 100644 --- a/test/build.js +++ b/test/build.js @@ -1,6 +1,5 @@ 'use strict'; -var string = require('string'); var path = require('path'); var fs = require('fs'); var assert = require('assert'); diff --git a/test/utils.js b/test/utils.js index d6821d9d4b..e76c1b9bff 100644 --- a/test/utils.js +++ b/test/utils.js @@ -16,6 +16,28 @@ describe('Utility Methods', function () { var $ = global.$; global.window = window; + // https://github.com/jprichardson/string.js/blob/master/test/string.test.js + it('should decode HTML entities', function (done) { + assert.strictEqual( + utils.decodeHTMLEntities('Ken Thompson & Dennis Ritchie'), + 'Ken Thompson & Dennis Ritchie' + ); + assert.strictEqual( + utils.decodeHTMLEntities('3 < 4'), + '3 < 4' + ); + assert.strictEqual( + utils.decodeHTMLEntities('http://'), + 'http://' + ); + done(); + }); + it('should strip HTML tags', function (done) { + assert.strictEqual(utils.stripHTMLTags('

just some text

'), 'just some text'); + assert.strictEqual(utils.stripHTMLTags('

just some text

', ['p']), 'just some text'); + done(); + }); + it('should preserve case if requested', function (done) { var slug = utils.slugify('UPPER CASE', true); assert.equal(slug, 'UPPER-CASE'); From 190eea5691ac5a4a8f89154d5ef7a1b77c7c32fa Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Fri, 13 Oct 2017 21:17:57 -0600 Subject: [PATCH 3/3] Remove this useless check --- public/src/modules/translator.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/public/src/modules/translator.js b/public/src/modules/translator.js index cd0d3c75c1..18a42e4e8f 100644 --- a/public/src/modules/translator.js +++ b/public/src/modules/translator.js @@ -42,7 +42,6 @@ } }(function (utils, load, warn) { var assign = Object.assign || jQuery.extend; - function classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } function escapeHTML(str) { return utils.decodeHTMLEntities( @@ -62,7 +61,6 @@ */ function Translator(language) { var self = this; - classCallCheck(self, Translator); if (!language) { throw new TypeError('Parameter `language` must be a language string. Received ' + language + (language === '' ? '(empty string)' : ''));