From 673293575ac674b2557d9d7d1da44e757a2fd893 Mon Sep 17 00:00:00 2001 From: Clay Diffrient Date: Fri, 9 Jan 2015 20:31:47 -0700 Subject: [PATCH] Makes most special characters be replaced with a dash closes #4782 - Still achieves the same goal of stripping out reserved characters - Changes from removal to replacement - This helps word separators from being removed - Apostrophes (') are unaffected --- core/server/models/base.js | 3 -- core/server/utils/index.js | 20 ++++++--- core/test/unit/server_utils_spec.js | 66 +++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 8 deletions(-) create mode 100644 core/test/unit/server_utils_spec.js diff --git a/core/server/models/base.js b/core/server/models/base.js index ee6fcfd059..686ecdf6d1 100644 --- a/core/server/models/base.js +++ b/core/server/models/base.js @@ -351,9 +351,6 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({ slug = utils.safeString(base); - // Remove trailing hyphen - slug = slug.charAt(slug.length - 1) === '-' ? slug.substr(0, slug.length - 1) : slug; - // If it's a user, let's try to cut it down (unless this is a human request) if (baseName === 'user' && options && options.shortSlug && slugTryCount === 1 && slug !== 'ghost-owner') { longSlug = slug; diff --git a/core/server/utils/index.js b/core/server/utils/index.js index b95f8df379..9423dde025 100644 --- a/core/server/utils/index.js +++ b/core/server/utils/index.js @@ -49,20 +49,30 @@ utils = { return buf.join(''); }, safeString: function (string) { - string = string.trim(); + // Handle the £ symbol seperately, since it needs to be removed before + // the unicode conversion. + string = string.replace(/£/g, '-'); // Remove non ascii characters string = unidecode(string); - // Remove URL reserved chars: `:/?#[]@!$&'()*+,;=` as well as `\%<>|^~£"` - string = string.replace(/[:\/\?#\[\]@!$&'()*+,;=\\%<>\|\^~£"]/g, '') - // Replace dots and spaces with a dash - .replace(/(\s|\.)/g, '-') + // Replace URL reserved chars: `:/?#[]!$&()*+,;=` as well as `\%<>|^~£"` + string = string.replace(/(\s|\.|@|:|\/|\?|#|\[|\]|!|\$|&|\(|\)|\*|\+|,|;|=|\\|%|<|>|\||\^|~|"|–|—)/g, '-') + // Remove apostrophes + .replace(/'/g, '') // Convert 2 or more dashes into a single dash .replace(/-+/g, '-') + // Remove any dashes at the beginning + .replace(/^-/, '') // Make the whole thing lowercase .toLowerCase(); + // Remove trailing dash if needed + string = string.charAt(string.length - 1) === '-' ? string.substr(0, string.length - 1) : string; + + // Handle whitespace at the beginning or end. + string = string.trim(); + return string; }, // The token is encoded URL safe by replcaing '+' with '-', '\' with '_' and removing '=' diff --git a/core/test/unit/server_utils_spec.js b/core/test/unit/server_utils_spec.js new file mode 100644 index 0000000000..c2e8ed9199 --- /dev/null +++ b/core/test/unit/server_utils_spec.js @@ -0,0 +1,66 @@ +/*globals describe, it*/ +/*jshint expr:true*/ +var should = require('should'), + utils = require('../../server/utils'); + +// To stop jshint complaining +should.equal(true, true); + +describe('Safe String', function () { + var safeString = utils.safeString; + + it('should remove beginning and ending whitespace', function () { + var result = safeString(' stringwithspace '); + result.should.equal('stringwithspace'); + }); + + it('should remove non ascii characters', function () { + var result = safeString('howtowin✓'); + result.should.equal('howtowin'); + }); + + it('should replace spaces with dashes', function () { + var result = safeString('how to win'); + result.should.equal('how-to-win'); + }); + + it('should replace most special characters with dashes', function () { + var result = safeString('a:b/c?d#e[f]g!h$i&j(k)l*m+n,o;p=q\\r%su|v^w~x£y"z@1.2'); + result.should.equal('a-b-c-d-e-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z-1-2'); + }); + + it('should remove special characters at the beginning of a string', function () { + var result = safeString('.Not special'); + result.should.equal('not-special'); + }); + + it('should remove apostrophes ', function () { + var result = safeString('how we shouldn\'t be'); + result.should.equal('how-we-shouldnt-be'); + }); + + it('should convert to lowercase', function () { + var result = safeString('This has Upper Case'); + result.should.equal('this-has-upper-case'); + }); + + it('should convert multiple dashes into a single dash', function () { + var result = safeString('This :) means everything'); + result.should.equal('this-means-everything'); + }); + + it('should remove trailing dashes from the result', function () { + var result = safeString('This.'); + result.should.equal('this'); + }); + + it('should handle pound signs', function () { + var result = safeString('WHOOPS! I spent all my £ again!'); + result.should.equal('whoops-i-spent-all-my-again'); + }); + + it('should properly handle unicode punctuation conversion', function () { + var result = safeString('に間違いがないか、再度確認してください。再読み込みしてください。'); + result.should.equal('nijian-wei-iganaika-zai-du-que-ren-sitekudasai-zai-du-miip-misitekudasai'); + }); +});