diff --git a/core/server/adapters/scheduling/post-scheduling/index.js b/core/server/adapters/scheduling/post-scheduling/index.js index 59c9d2fbff..9e6424b7d7 100644 --- a/core/server/adapters/scheduling/post-scheduling/index.js +++ b/core/server/adapters/scheduling/post-scheduling/index.js @@ -3,7 +3,7 @@ const Promise = require('bluebird'), localUtils = require('../utils'), common = require('../../../lib/common'), models = require('../../../models'), - urlService = require('../../../services/url'), + urlUtils = require('../../../lib/url-utils'), _private = {}; /** @@ -18,7 +18,7 @@ _private.normalize = function normalize(options) { // NOTE: The scheduler expects a unix timestmap. time: moment(model.get('published_at')).valueOf(), // @TODO: We are still using API v0.1 - url: `${urlService.utils.urlJoin(apiUrl, 'schedules', 'posts', model.get('id'))}?client_id=${client.get('slug')}&client_secret=${client.get('secret')}`, + url: `${urlUtils.urlJoin(apiUrl, 'schedules', 'posts', model.get('id'))}?client_id=${client.get('slug')}&client_secret=${client.get('secret')}`, extra: { httpMethod: 'PUT', oldTime: model.previous('published_at') ? moment(model.previous('published_at')).valueOf() : null diff --git a/core/server/adapters/storage/LocalFileStorage.js b/core/server/adapters/storage/LocalFileStorage.js index 71d2af0f65..cece529cbd 100644 --- a/core/server/adapters/storage/LocalFileStorage.js +++ b/core/server/adapters/storage/LocalFileStorage.js @@ -9,7 +9,7 @@ const serveStatic = require('express').static, config = require('../../config'), common = require('../../lib/common'), constants = require('../../lib/constants'), - urlService = require('../../services/url'), + urlUtils = require('../../lib/url-utils'), StorageBase = require('ghost-storage-base'); class LocalFileStore extends StorageBase { @@ -35,8 +35,8 @@ class LocalFileStore extends StorageBase { .then(() => { // For local file system storage can use relative path so add a slash const fullUrl = ( - urlService.utils.urlJoin('/', urlService.utils.getSubdir(), - urlService.utils.STATIC_IMAGE_URL_PREFIX, + urlUtils.urlJoin('/', urlUtils.getSubdir(), + urlUtils.STATIC_IMAGE_URL_PREFIX, targetPath) ).replace(new RegExp(`\\${path.sep}`, 'g'), '/'); @@ -68,8 +68,8 @@ class LocalFileStore extends StorageBase { // The src for the image must be in URI format, not a file system path, which in Windows uses \ // For local file system storage can use relative path so add a slash const fullUrl = ( - urlService.utils.urlJoin('/', urlService.utils.getSubdir(), - urlService.utils.STATIC_IMAGE_URL_PREFIX, + urlUtils.urlJoin('/', urlUtils.getSubdir(), + urlUtils.STATIC_IMAGE_URL_PREFIX, path.relative(this.storagePath, targetFilename)) ).replace(new RegExp(`\\${path.sep}`, 'g'), '/'); diff --git a/core/server/adapters/storage/utils.js b/core/server/adapters/storage/utils.js index de2736b9e3..1aa729bee6 100644 --- a/core/server/adapters/storage/utils.js +++ b/core/server/adapters/storage/utils.js @@ -1,5 +1,4 @@ -const urlService = require('../../services/url'); - +const urlUtils = require('../../lib/url-utils'); /** * @TODO: move `events.js` to here - e.g. storageUtils.getStorage */ @@ -15,16 +14,16 @@ const urlService = require('../../services/url'); */ exports.getLocalFileStoragePath = function getLocalFileStoragePath(imagePath) { // The '/' in urlJoin is necessary to add the '/' to `content/images`, if no subdirectory is setup - const urlRegExp = new RegExp(`^${urlService.utils.urlJoin( - urlService.utils.urlFor('home', true), - urlService.utils.getSubdir(), + const urlRegExp = new RegExp(`^${urlUtils.urlJoin( + urlUtils.urlFor('home', true), + urlUtils.getSubdir(), '/', - urlService.utils.STATIC_IMAGE_URL_PREFIX)}` + urlUtils.STATIC_IMAGE_URL_PREFIX)}` ), - filePathRegExp = new RegExp(`^${urlService.utils.urlJoin( - urlService.utils.getSubdir(), + filePathRegExp = new RegExp(`^${urlUtils.urlJoin( + urlUtils.getSubdir(), '/', - urlService.utils.STATIC_IMAGE_URL_PREFIX)}` + urlUtils.STATIC_IMAGE_URL_PREFIX)}` ); if (imagePath.match(urlRegExp)) { diff --git a/core/server/api/v0.1/authentication.js b/core/server/api/v0.1/authentication.js index 284795bd5c..c133ba7204 100644 --- a/core/server/api/v0.1/authentication.js +++ b/core/server/api/v0.1/authentication.js @@ -6,8 +6,8 @@ const Promise = require('bluebird'), security = require('../../lib/security'), constants = require('../../lib/constants'), pipeline = require('../../lib/promise/pipeline'), + urlUtils = require('../../lib/url-utils'), mail = require('../../services/mail'), - urlService = require('../../services/url'), localUtils = require('./utils'), models = require('../../models'), web = require('../../web'), @@ -185,8 +185,8 @@ authentication = { } function sendResetNotification(data) { - const adminUrl = urlService.utils.urlFor('admin', true), - resetUrl = urlService.utils.urlJoin(adminUrl, 'reset', security.url.encodeBase64(data.resetToken), '/'); + const adminUrl = urlUtils.urlFor('admin', true), + resetUrl = urlUtils.urlJoin(adminUrl, 'reset', security.url.encodeBase64(data.resetToken), '/'); return mail.utils.generateContent({ data: { diff --git a/core/server/api/v0.1/configuration.js b/core/server/api/v0.1/configuration.js index 9e6ddd17ba..2f22949a7c 100644 --- a/core/server/api/v0.1/configuration.js +++ b/core/server/api/v0.1/configuration.js @@ -2,7 +2,7 @@ // RESTful API for browsing the configuration const Promise = require('bluebird'), {isPlainObject} = require('lodash'), - urlService = require('../../services/url'), + urlUtils = require('../../lib/url-utils'), models = require('../../models'), config = require('../../config'), labs = require('../../services/labs'), @@ -29,7 +29,7 @@ function getBaseConfig() { return { useGravatar: !config.isPrivacyDisabled('useGravatar'), publicAPI: labs.isSet('publicAPI'), - blogUrl: urlService.utils.urlFor('home', true), + blogUrl: urlUtils.urlFor('home', true), blogTitle: settingsCache.get('title'), clientExtensions: config.get('clientExtensions'), enableDeveloperExperiments: config.get('enableDeveloperExperiments') diff --git a/core/server/api/v0.1/decorators/urls.js b/core/server/api/v0.1/decorators/urls.js index 6971946e98..dd22b80381 100644 --- a/core/server/api/v0.1/decorators/urls.js +++ b/core/server/api/v0.1/decorators/urls.js @@ -1,5 +1,5 @@ const urlService = require('../../../services/url'); -const {urlFor, makeAbsoluteUrls} = require('../../../services/url/utils'); +const {urlFor, makeAbsoluteUrls} = require('../../../lib/url-utils'); const urlsForPost = (id, attrs, options) => { attrs.url = urlService.getUrlByResourceId(id); diff --git a/core/server/api/v0.1/index.js b/core/server/api/v0.1/index.js index 17ed4f1125..337bdb8ff8 100644 --- a/core/server/api/v0.1/index.js +++ b/core/server/api/v0.1/index.js @@ -7,7 +7,7 @@ const {isEmpty} = require('lodash'); const Promise = require('bluebird'); const models = require('../../models'); -const urlService = require('../../services/url'); +const urlUtils = require('../../lib/url-utils'); const configuration = require('./configuration'); const db = require('./db'); const mail = require('./mail'); @@ -97,7 +97,7 @@ const cacheInvalidationHeader = (req, result) => { return INVALIDATE_ALL; } else { // routeKeywords.preview: 'p' - return urlService.utils.urlFor({relativeUrl: urlService.utils.urlJoin('/p', post.uuid, '/')}); + return urlUtils.urlFor({relativeUrl: urlUtils.urlJoin('/p', post.uuid, '/')}); } } } @@ -115,7 +115,7 @@ const cacheInvalidationHeader = (req, result) => { * @return {String} Resolves to header string */ const locationHeader = (req, result) => { - const apiRoot = urlService.utils.urlFor('api', {version: 'v0.1'}); + const apiRoot = urlUtils.urlFor('api', {version: 'v0.1'}); let location, newObject, statusQuery; @@ -124,23 +124,23 @@ const locationHeader = (req, result) => { if (result.hasOwnProperty('posts')) { newObject = result.posts[0]; statusQuery = `/?status=${newObject.status}`; - location = urlService.utils.urlJoin(apiRoot, 'posts', newObject.id, statusQuery); + location = urlUtils.urlJoin(apiRoot, 'posts', newObject.id, statusQuery); } else if (result.hasOwnProperty('notifications')) { newObject = result.notifications[0]; // CASE: you add one notification, but it's a duplicate, the API will return {notifications: []} if (newObject) { - location = urlService.utils.urlJoin(apiRoot, 'notifications', newObject.id, '/'); + location = urlUtils.urlJoin(apiRoot, 'notifications', newObject.id, '/'); } } else if (result.hasOwnProperty('users')) { newObject = result.users[0]; - location = urlService.utils.urlJoin(apiRoot, 'users', newObject.id, '/'); + location = urlUtils.urlJoin(apiRoot, 'users', newObject.id, '/'); } else if (result.hasOwnProperty('tags')) { newObject = result.tags[0]; - location = urlService.utils.urlJoin(apiRoot, 'tags', newObject.id, '/'); + location = urlUtils.urlJoin(apiRoot, 'tags', newObject.id, '/'); } else if (result.hasOwnProperty('webhooks')) { newObject = result.webhooks[0]; - location = urlService.utils.urlJoin(apiRoot, 'webhooks', newObject.id, '/'); + location = urlUtils.urlJoin(apiRoot, 'webhooks', newObject.id, '/'); } } diff --git a/core/server/api/v0.1/invites.js b/core/server/api/v0.1/invites.js index 9bde6f7714..00c1cc991b 100644 --- a/core/server/api/v0.1/invites.js +++ b/core/server/api/v0.1/invites.js @@ -2,7 +2,7 @@ const Promise = require('bluebird'), {omit, merge} = require('lodash'), pipeline = require('../../lib/promise/pipeline'), mail = require('../../services/mail'), - urlService = require('../../services/url'), + urlUtils = require('../../lib/url-utils'), localUtils = require('./utils'), models = require('../../models'), common = require('../../lib/common'), @@ -106,14 +106,14 @@ const invites = { return settingsAPI.read({key: 'title'}); }) .then((response) => { - const adminUrl = urlService.utils.urlFor('admin', true); + const adminUrl = urlUtils.urlFor('admin', true); emailData = { blogName: response.settings[0].value, invitedByName: loggedInUser.get('name'), invitedByEmail: loggedInUser.get('email'), // @TODO: resetLink sounds weird - resetLink: urlService.utils.urlJoin(adminUrl, 'signup', security.url.encodeBase64(invite.get('token')), '/') + resetLink: urlUtils.urlJoin(adminUrl, 'signup', security.url.encodeBase64(invite.get('token')), '/') }; return mail.utils.generateContent({data: emailData, template: 'invite-user'}); diff --git a/core/server/api/v2/invites.js b/core/server/api/v2/invites.js index 4b913bed77..5f365628ca 100644 --- a/core/server/api/v2/invites.js +++ b/core/server/api/v2/invites.js @@ -2,7 +2,7 @@ const Promise = require('bluebird'); const common = require('../../lib/common'); const security = require('../../lib/security'); const mailService = require('../../services/mail'); -const urlService = require('../../services/url'); +const urlUtils = require('../../lib/url-utils'); const settingsCache = require('../../services/settings/cache'); const models = require('../../models'); const api = require('./index'); @@ -122,13 +122,13 @@ module.exports = { .then((_invite) => { invite = _invite; - const adminUrl = urlService.utils.urlFor('admin', true); + const adminUrl = urlUtils.urlFor('admin', true); emailData = { blogName: settingsCache.get('title'), invitedByName: frame.user.get('name'), invitedByEmail: frame.user.get('email'), - resetLink: urlService.utils.urlJoin(adminUrl, 'signup', security.url.encodeBase64(invite.get('token')), '/') + resetLink: urlUtils.urlJoin(adminUrl, 'signup', security.url.encodeBase64(invite.get('token')), '/') }; return mailService.utils.generateContent({data: emailData, template: 'invite-user'}); diff --git a/core/server/api/v2/pages.js b/core/server/api/v2/pages.js index a5fb776909..5e5dba7764 100644 --- a/core/server/api/v2/pages.js +++ b/core/server/api/v2/pages.js @@ -1,6 +1,6 @@ const models = require('../../models'); const common = require('../../lib/common'); -const urlService = require('../../services/url'); +const urlUtils = require('../../lib/url-utils'); const ALLOWED_INCLUDES = ['tags', 'authors', 'authors.roles']; const UNSAFE_ATTRS = ['status', 'authors']; @@ -142,8 +142,8 @@ module.exports = { model.get('status') === 'scheduled' && model.wasChanged() ) { this.headers.cacheInvalidate = { - value: urlService.utils.urlFor({ - relativeUrl: urlService.utils.urlJoin('/p', model.get('uuid'), '/') + value: urlUtils.urlFor({ + relativeUrl: urlUtils.urlJoin('/p', model.get('uuid'), '/') }) }; } else { diff --git a/core/server/api/v2/posts.js b/core/server/api/v2/posts.js index 09f244fbfd..70fd329168 100644 --- a/core/server/api/v2/posts.js +++ b/core/server/api/v2/posts.js @@ -1,6 +1,6 @@ const models = require('../../models'); const common = require('../../lib/common'); -const urlService = require('../../services/url'); +const urlUtils = require('../../lib/url-utils'); const allowedIncludes = ['tags', 'authors', 'authors.roles']; const unsafeAttrs = ['status', 'authors']; @@ -146,8 +146,8 @@ module.exports = { model.get('status') === 'scheduled' && model.wasChanged() ) { this.headers.cacheInvalidate = { - value: urlService.utils.urlFor({ - relativeUrl: urlService.utils.urlJoin('/p', model.get('uuid'), '/') + value: urlUtils.urlFor({ + relativeUrl: urlUtils.urlJoin('/p', model.get('uuid'), '/') }) }; } else { diff --git a/core/server/api/v2/site.js b/core/server/api/v2/site.js index bad1dd5245..68cb903640 100644 --- a/core/server/api/v2/site.js +++ b/core/server/api/v2/site.js @@ -1,6 +1,6 @@ const ghostVersion = require('../../lib/ghost-version'); const settingsCache = require('../../services/settings/cache'); -const urlService = require('../../services/url'); +const urlUtils = require('../../lib/url-utils'); const site = { docName: 'site', @@ -10,7 +10,7 @@ const site = { query() { return { title: settingsCache.get('title'), - url: urlService.utils.urlFor('home', true), + url: urlUtils.urlFor('home', true), version: ghostVersion.safe }; } diff --git a/core/server/api/v2/utils/serializers/input/utils/url.js b/core/server/api/v2/utils/serializers/input/utils/url.js index 4c421a9cc1..31c27bfb0a 100644 --- a/core/server/api/v2/utils/serializers/input/utils/url.js +++ b/core/server/api/v2/utils/serializers/input/utils/url.js @@ -1,9 +1,9 @@ const _ = require('lodash'); const url = require('url'); -const utils = require('../../../../../../services/url/utils'); +const urlUtils = require('../../../../../../lib/url-utils'); const handleCanonicalUrl = (canonicalUrl) => { - const blogURl = utils.getBlogUrl(); + const blogURl = urlUtils.getBlogUrl(); const isSameProtocol = url.parse(canonicalUrl).protocol === url.parse(blogURl).protocol; const blogDomain = blogURl.replace(/^http(s?):\/\//, '').replace(/\/$/, ''); const absolute = canonicalUrl.replace(/^http(s?):\/\//, ''); @@ -12,33 +12,33 @@ const handleCanonicalUrl = (canonicalUrl) => { // Blog URL incl. the same protocol. This allows users to keep e.g. Facebook comments after // a http -> https switch if (absolute.startsWith(blogDomain) && isSameProtocol) { - return utils.absoluteToRelative(canonicalUrl, {withoutSubdirectory: true}); + return urlUtils.absoluteToRelative(canonicalUrl, {withoutSubdirectory: true}); } return canonicalUrl; }; const handleImageUrl = (imageUrl) => { - const blogDomain = utils.getBlogUrl().replace(/^http(s?):\/\//, '').replace(/\/$/, ''); + const blogDomain = urlUtils.getBlogUrl().replace(/^http(s?):\/\//, '').replace(/\/$/, ''); const imageUrlAbsolute = imageUrl.replace(/^http(s?):\/\//, ''); - const imagePathRe = new RegExp(`^${blogDomain}/${utils.STATIC_IMAGE_URL_PREFIX}`); + const imagePathRe = new RegExp(`^${blogDomain}/${urlUtils.STATIC_IMAGE_URL_PREFIX}`); if (imagePathRe.test(imageUrlAbsolute)) { - return utils.absoluteToRelative(imageUrl); + return urlUtils.absoluteToRelative(imageUrl); } return imageUrl; }; const handleContentUrls = (content) => { - const blogDomain = utils.getBlogUrl().replace(/^http(s?):\/\//, '').replace(/\/$/, ''); - const imagePathRe = new RegExp(`(http(s?)://)?${blogDomain}/${utils.STATIC_IMAGE_URL_PREFIX}`, 'g'); + const blogDomain = urlUtils.getBlogUrl().replace(/^http(s?):\/\//, '').replace(/\/$/, ''); + const imagePathRe = new RegExp(`(http(s?)://)?${blogDomain}/${urlUtils.STATIC_IMAGE_URL_PREFIX}`, 'g'); const matches = _.uniq(content.match(imagePathRe)); if (matches) { matches.forEach((match) => { - const relative = utils.absoluteToRelative(match); + const relative = urlUtils.absoluteToRelative(match); content = content.replace(new RegExp(match, 'g'), relative); }); } diff --git a/core/server/api/v2/utils/serializers/output/utils/url.js b/core/server/api/v2/utils/serializers/output/utils/url.js index ba12e2bd2a..d6d2da321d 100644 --- a/core/server/api/v2/utils/serializers/output/utils/url.js +++ b/core/server/api/v2/utils/serializers/output/utils/url.js @@ -1,5 +1,6 @@ const _ = require('lodash'); const urlService = require('../../../../../../services/url'); +const urlUtils = require('../../../../../../lib/url-utils'); const localUtils = require('../../../index'); const forPost = (id, attrs, frame) => { @@ -26,25 +27,25 @@ const forPost = (id, attrs, frame) => { attrs.url = urlService .utils .urlFor({ - relativeUrl: urlService.utils.urlJoin('/p', attrs.uuid, '/') + relativeUrl: urlUtils.urlJoin('/p', attrs.uuid, '/') }, null, true); } } if (attrs.feature_image) { - attrs.feature_image = urlService.utils.urlFor('image', {image: attrs.feature_image}, true); + attrs.feature_image = urlUtils.urlFor('image', {image: attrs.feature_image}, true); } if (attrs.og_image) { - attrs.og_image = urlService.utils.urlFor('image', {image: attrs.og_image}, true); + attrs.og_image = urlUtils.urlFor('image', {image: attrs.og_image}, true); } if (attrs.twitter_image) { - attrs.twitter_image = urlService.utils.urlFor('image', {image: attrs.twitter_image}, true); + attrs.twitter_image = urlUtils.urlFor('image', {image: attrs.twitter_image}, true); } if (attrs.canonical_url) { - attrs.canonical_url = urlService.utils.relativeToAbsolute(attrs.canonical_url); + attrs.canonical_url = urlUtils.relativeToAbsolute(attrs.canonical_url); } if (attrs.html) { @@ -56,9 +57,9 @@ const forPost = (id, attrs, frame) => { urlOptions.assetsOnly = false; } - attrs.html = urlService.utils.makeAbsoluteUrls( + attrs.html = urlUtils.makeAbsoluteUrls( attrs.html, - urlService.utils.urlFor('home', true), + urlUtils.urlFor('home', true), attrs.url, urlOptions ).html(); @@ -77,11 +78,11 @@ const forUser = (id, attrs, options) => { } if (attrs.profile_image) { - attrs.profile_image = urlService.utils.urlFor('image', {image: attrs.profile_image}, true); + attrs.profile_image = urlUtils.urlFor('image', {image: attrs.profile_image}, true); } if (attrs.cover_image) { - attrs.cover_image = urlService.utils.urlFor('image', {image: attrs.cover_image}, true); + attrs.cover_image = urlUtils.urlFor('image', {image: attrs.cover_image}, true); } return attrs; @@ -93,7 +94,7 @@ const forTag = (id, attrs, options) => { } if (attrs.feature_image) { - attrs.feature_image = urlService.utils.urlFor('image', {image: attrs.feature_image}, true); + attrs.feature_image = urlUtils.urlFor('image', {image: attrs.feature_image}, true); } return attrs; @@ -105,20 +106,20 @@ const forSettings = (attrs) => { if (_.isArray(attrs)) { attrs.forEach((obj) => { if (['cover_image', 'logo', 'icon'].includes(obj.key) && obj.value) { - obj.value = urlService.utils.urlFor('image', {image: obj.value}, true); + obj.value = urlUtils.urlFor('image', {image: obj.value}, true); } }); } else { if (attrs.cover_image) { - attrs.cover_image = urlService.utils.urlFor('image', {image: attrs.cover_image}, true); + attrs.cover_image = urlUtils.urlFor('image', {image: attrs.cover_image}, true); } if (attrs.logo) { - attrs.logo = urlService.utils.urlFor('image', {image: attrs.logo}, true); + attrs.logo = urlUtils.urlFor('image', {image: attrs.logo}, true); } if (attrs.icon) { - attrs.icon = urlService.utils.urlFor('image', {image: attrs.icon}, true); + attrs.icon = urlUtils.urlFor('image', {image: attrs.icon}, true); } } @@ -126,7 +127,7 @@ const forSettings = (attrs) => { }; const forImage = (path) => { - return urlService.utils.urlFor('image', {image: path}, true); + return urlUtils.urlFor('image', {image: path}, true); }; module.exports.forPost = forPost; diff --git a/core/server/apps/amp/index.js b/core/server/apps/amp/index.js index 2bd76ceebe..fa791ed9c5 100644 --- a/core/server/apps/amp/index.js +++ b/core/server/apps/amp/index.js @@ -1,6 +1,6 @@ const router = require('./lib/router'), registerHelpers = require('./lib/helpers'), - urlService = require('../../services/url'), + urlUtils = require('../../lib/url-utils'), // Dirty requires settingsCache = require('../../services/settings/cache'); @@ -11,7 +11,7 @@ function ampRouter(req, res) { } else { // routeKeywords.amp: 'amp' let redirectUrl = req.originalUrl.replace(/amp\/$/, ''); - urlService.utils.redirect301(res, redirectUrl); + urlUtils.redirect301(res, redirectUrl); } } diff --git a/core/server/apps/amp/lib/helpers/amp_content.js b/core/server/apps/amp/lib/helpers/amp_content.js index 4ec659a4cd..e4ef60bb2d 100644 --- a/core/server/apps/amp/lib/helpers/amp_content.js +++ b/core/server/apps/amp/lib/helpers/amp_content.js @@ -13,7 +13,7 @@ const Promise = require('bluebird'), logging = proxy.logging, i18n = proxy.i18n, errors = proxy.errors, - urlService = require('../../../../services/url'), + urlUtils = require('../../../../lib/url-utils'), amperizeCache = {}; let allowedAMPTags = [], @@ -124,7 +124,7 @@ function getAmperizeHTML(html, post) { amperize = amperize || new Amperize(); // make relative URLs abolute - html = urlService.utils.makeAbsoluteUrls(html, urlService.utils.urlFor('home', true), post.url).html(); + html = urlUtils.makeAbsoluteUrls(html, urlUtils.urlFor('home', true), post.url).html(); if (!amperizeCache[post.id] || moment(new Date(amperizeCache[post.id].updated_at)).diff(new Date(post.updated_at)) < 0) { return new Promise((resolve) => { diff --git a/core/server/apps/private-blogging/index.js b/core/server/apps/private-blogging/index.js index 9ef3f543b9..5d9657033e 100644 --- a/core/server/apps/private-blogging/index.js +++ b/core/server/apps/private-blogging/index.js @@ -1,4 +1,4 @@ -const urlService = require('../../services/url'), +const urlUtils = require('../../lib/url-utils'), common = require('../../lib/common'), middleware = require('./lib/middleware'), router = require('./lib/router'), @@ -9,8 +9,8 @@ const urlService = require('../../services/url'), let checkSubdir = function checkSubdir() { let paths = ''; - if (urlService.utils.getSubdir()) { - paths = urlService.utils.getSubdir().split('/'); + if (urlUtils.getSubdir()) { + paths = urlUtils.getSubdir().split('/'); if (paths.pop() === PRIVATE_KEYWORD) { common.logging.error(new common.errors.GhostError({ diff --git a/core/server/apps/private-blogging/lib/middleware.js b/core/server/apps/private-blogging/lib/middleware.js index 0354aa68ac..74f40fae2a 100644 --- a/core/server/apps/private-blogging/lib/middleware.js +++ b/core/server/apps/private-blogging/lib/middleware.js @@ -4,7 +4,7 @@ const session = require('cookie-session'); const crypto = require('crypto'); const path = require('path'); const config = require('../../../config'); -const urlService = require('../../../services/url'); +const urlUtils = require('../../../lib/url-utils'); const constants = require('../../../lib/constants'); const common = require('../../../lib/common'); const settingsCache = require('../../../services/settings/cache'); @@ -99,7 +99,7 @@ const privateBlogging = { if (isVerified) { return next(); } else { - url = urlService.utils.urlFor({relativeUrl: privateRoute}); + url = urlUtils.urlFor({relativeUrl: privateRoute}); url += '?r=' + encodeURIComponent(req.url); return res.redirect(url); } @@ -108,7 +108,7 @@ const privateBlogging = { // This is here so a call to /private/ after a session is verified will redirect to home; isPrivateSessionAuth: function isPrivateSessionAuth(req, res, next) { if (!res.isPrivateBlog) { - return res.redirect(urlService.utils.urlFor('home', true)); + return res.redirect(urlUtils.urlFor('home', true)); } let hash = req.session.token || '', @@ -117,7 +117,7 @@ const privateBlogging = { if (isVerified) { // redirect to home if user is already authenticated - return res.redirect(urlService.utils.urlFor('home', true)); + return res.redirect(urlUtils.urlFor('home', true)); } else { return next(); } @@ -140,7 +140,7 @@ const privateBlogging = { req.session.token = hasher.digest('hex'); req.session.salt = salt; - return res.redirect(urlService.utils.urlFor({relativeUrl: forward})); + return res.redirect(urlUtils.urlFor({relativeUrl: forward})); } else { res.error = { message: common.i18n.t('errors.middleware.privateblogging.wrongPassword') diff --git a/core/server/apps/subscribers/lib/helpers/subscribe_form.js b/core/server/apps/subscribers/lib/helpers/subscribe_form.js index 2e1b090c38..53434640fd 100644 --- a/core/server/apps/subscribers/lib/helpers/subscribe_form.js +++ b/core/server/apps/subscribers/lib/helpers/subscribe_form.js @@ -5,7 +5,7 @@ const _ = require('lodash'), proxy = require('../../../../helpers/proxy'), createFrame = proxy.hbs.handlebars.createFrame, templates = proxy.templates, - urlService = proxy.urlService, + urlUtils = proxy.urlUtils, SafeString = proxy.SafeString, params = ['error', 'success', 'email']; @@ -43,7 +43,7 @@ module.exports = function subscribe_form(options) { // eslint-disable-line camel const context = _.merge({}, options.hash, _.pick(root, params), { // routeKeywords.subscribe: 'subscribe' - action: urlService.utils.urlJoin('/', urlService.utils.getSubdir(), 'subscribe/'), + action: urlUtils.urlJoin('/', urlUtils.getSubdir(), 'subscribe/'), script: new SafeString(subscribeScript), hidden: new SafeString( makeHidden('confirm') + diff --git a/core/server/apps/subscribers/lib/router.js b/core/server/apps/subscribers/lib/router.js index 5499d10bc1..6c3cfdeefd 100644 --- a/core/server/apps/subscribers/lib/router.js +++ b/core/server/apps/subscribers/lib/router.js @@ -5,6 +5,7 @@ const path = require('path'), bodyParser = require('body-parser'), // Dirty requires common = require('../../../lib/common'), + urlUtils = require('../../../lib/url-utils'), urlService = require('../../../services/url'), validator = require('../../../data/validation').validator, routing = require('../../../services/routing'), @@ -64,7 +65,7 @@ function handleSource(req, res, next) { delete req.body.location; delete req.body.referrer; - const resource = urlService.getResource(urlService.utils.absoluteToRelative(req.body.subscribed_url, {withoutSubdirectory: true})); + const resource = urlService.getResource(urlUtils.absoluteToRelative(req.body.subscribed_url, {withoutSubdirectory: true})); if (resource) { req.body.post_id = resource.data.id; diff --git a/core/server/data/db/backup.js b/core/server/data/db/backup.js index cdfd5b4f7c..91eb5ff0c6 100644 --- a/core/server/data/db/backup.js +++ b/core/server/data/db/backup.js @@ -5,14 +5,14 @@ var fs = require('fs-extra'), Promise = require('bluebird'), config = require('../../config'), common = require('../../lib/common'), - urlService = require('../../services/url'), + urlUtils = require('../../lib/url-utils'), exporter = require('../exporter'), writeExportFile, backup; writeExportFile = function writeExportFile(exportResult) { - var filename = path.resolve(urlService.utils.urlJoin(config.get('paths').contentPath, 'data', exportResult.filename)); + var filename = path.resolve(urlUtils.urlJoin(config.get('paths').contentPath, 'data', exportResult.filename)); return fs.writeFile(filename, JSON.stringify(exportResult.data)).return(filename); }; diff --git a/core/server/data/importer/handlers/image.js b/core/server/data/importer/handlers/image.js index 730067cc67..d9c1fa44d0 100644 --- a/core/server/data/importer/handlers/image.js +++ b/core/server/data/importer/handlers/image.js @@ -2,7 +2,7 @@ var _ = require('lodash'), Promise = require('bluebird'), path = require('path'), config = require('../../../config'), - urlService = require('../../../services/url'), + urlUtils = require('../../../lib/url-utils'), storage = require('../../../adapters/storage'), ImageHandler; @@ -16,7 +16,7 @@ ImageHandler = { loadFile: function (files, baseDir) { var store = storage.getStorage(), baseDirRegex = baseDir ? new RegExp('^' + baseDir + '/') : new RegExp(''), - imageFolderRegexes = _.map(urlService.utils.STATIC_IMAGE_URL_PREFIX.split('/'), function (dir) { + imageFolderRegexes = _.map(urlUtils.STATIC_IMAGE_URL_PREFIX.split('/'), function (dir) { return new RegExp('^' + dir + '/'); }); @@ -37,7 +37,7 @@ ImageHandler = { return Promise.map(files, function (image) { return store.getUniqueFileName(image, image.targetDir).then(function (targetFilename) { - image.newPath = urlService.utils.urlJoin('/', urlService.utils.getSubdir(), urlService.utils.STATIC_IMAGE_URL_PREFIX, + image.newPath = urlUtils.urlJoin('/', urlUtils.getSubdir(), urlUtils.STATIC_IMAGE_URL_PREFIX, path.relative(config.getContentPath('images'), targetFilename)); return image; diff --git a/core/server/data/meta/amp_url.js b/core/server/data/meta/amp_url.js index 0868679e58..cace694171 100644 --- a/core/server/data/meta/amp_url.js +++ b/core/server/data/meta/amp_url.js @@ -1,4 +1,4 @@ -var urlService = require('../../services/url'), +var urlUtils = require('../../lib/url-utils'), getUrl = require('./url'), _ = require('lodash'); @@ -6,7 +6,7 @@ function getAmplUrl(data) { var context = data.context ? data.context : null; if (_.includes(context, 'post') && !_.includes(context, 'amp')) { - return urlService.utils.urlJoin(urlService.utils.urlFor('home', true), getUrl(data, false), 'amp/'); + return urlUtils.urlJoin(urlUtils.urlFor('home', true), getUrl(data, false), 'amp/'); } return null; } diff --git a/core/server/data/meta/asset_url.js b/core/server/data/meta/asset_url.js index 93b30978eb..8c9a730435 100644 --- a/core/server/data/meta/asset_url.js +++ b/core/server/data/meta/asset_url.js @@ -1,7 +1,7 @@ const crypto = require('crypto'), config = require('../../config'), imageLib = require('../../lib/image'), - urlService = require('../../services/url'); + urlUtils = require('../../lib/url-utils'); /** * Serve either uploaded favicon or default @@ -20,11 +20,11 @@ function getAssetUrl(path, hasMinFile) { // CASE: Build the output URL // Add subdirectory... - var output = urlService.utils.urlJoin(urlService.utils.getSubdir(), '/'); + var output = urlUtils.urlJoin(urlUtils.getSubdir(), '/'); // Optionally add /assets/ if (!path.match(/^public/) && !path.match(/^asset/)) { - output = urlService.utils.urlJoin(output, 'assets/'); + output = urlUtils.urlJoin(output, 'assets/'); } // replace ".foo" with ".min.foo" if configured @@ -33,7 +33,7 @@ function getAssetUrl(path, hasMinFile) { } // Add the path for the requested asset - output = urlService.utils.urlJoin(output, path); + output = urlUtils.urlJoin(output, path); // Ensure we have an assetHash // @TODO rework this! diff --git a/core/server/data/meta/author_image.js b/core/server/data/meta/author_image.js index 287493f2e7..8d8006ba85 100644 --- a/core/server/data/meta/author_image.js +++ b/core/server/data/meta/author_image.js @@ -1,4 +1,4 @@ -var urlService = require('../../services/url'), +var urlUtils = require('../../lib/url-utils'), getContextObject = require('./context_object.js'), _ = require('lodash'); @@ -7,7 +7,7 @@ function getAuthorImage(data, absolute) { contextObject = getContextObject(data, context); if ((_.includes(context, 'post') || _.includes(context, 'page')) && contextObject.primary_author && contextObject.primary_author.profile_image) { - return urlService.utils.urlFor('image', {image: contextObject.primary_author.profile_image}, absolute); + return urlUtils.urlFor('image', {image: contextObject.primary_author.profile_image}, absolute); } return null; } diff --git a/core/server/data/meta/blog_logo.js b/core/server/data/meta/blog_logo.js index fc5f5e42e5..56229f52bf 100644 --- a/core/server/data/meta/blog_logo.js +++ b/core/server/data/meta/blog_logo.js @@ -1,4 +1,4 @@ -var urlService = require('../../services/url'), +var urlUtils = require('../../lib/url-utils'), settingsCache = require('../../services/settings/cache'), imageLib = require('../../lib/image'); @@ -6,7 +6,7 @@ function getBlogLogo() { var logo = {}; if (settingsCache.get('logo')) { - logo.url = urlService.utils.urlFor('image', {image: settingsCache.get('logo')}, true); + logo.url = urlUtils.urlFor('image', {image: settingsCache.get('logo')}, true); } else { // CASE: no publication logo is updated. We can try to use either an uploaded publication icon // or use the default one to make diff --git a/core/server/data/meta/canonical_url.js b/core/server/data/meta/canonical_url.js index 7feb796bba..87a7e70b84 100644 --- a/core/server/data/meta/canonical_url.js +++ b/core/server/data/meta/canonical_url.js @@ -1,5 +1,5 @@ const _ = require('lodash'); -const urlService = require('../../services/url'); +const urlUtils = require('../../lib/url-utils'); const getUrl = require('./url'); function getCanonicalUrl(data) { @@ -8,7 +8,7 @@ function getCanonicalUrl(data) { return data.post.canonical_url; } - let url = urlService.utils.urlJoin(urlService.utils.urlFor('home', true), getUrl(data, false)); + let url = urlUtils.urlJoin(urlUtils.urlFor('home', true), getUrl(data, false)); if (url.indexOf('/amp/')) { url = url.replace(/\/amp\/$/i, '/'); diff --git a/core/server/data/meta/cover_image.js b/core/server/data/meta/cover_image.js index b61a7ece81..b2a3a71833 100644 --- a/core/server/data/meta/cover_image.js +++ b/core/server/data/meta/cover_image.js @@ -1,4 +1,4 @@ -var urlService = require('../../services/url'), +var urlUtils = require('../../lib/url-utils'), getContextObject = require('./context_object.js'), _ = require('lodash'); @@ -8,11 +8,11 @@ function getCoverImage(data) { if (_.includes(context, 'home') || _.includes(context, 'author')) { if (contextObject.cover_image) { - return urlService.utils.urlFor('image', {image: contextObject.cover_image}, true); + return urlUtils.urlFor('image', {image: contextObject.cover_image}, true); } } else { if (contextObject.feature_image) { - return urlService.utils.urlFor('image', {image: contextObject.feature_image}, true); + return urlUtils.urlFor('image', {image: contextObject.feature_image}, true); } } return null; diff --git a/core/server/data/meta/index.js b/core/server/data/meta/index.js index a2d8fb3b37..e063032b13 100644 --- a/core/server/data/meta/index.js +++ b/core/server/data/meta/index.js @@ -1,6 +1,6 @@ var Promise = require('bluebird'), settingsCache = require('../../services/settings/cache'), - urlService = require('../../services/url'), + urlUtils = require('../../lib/url-utils'), common = require('../../lib/common'), getUrl = require('./url'), getImageDimensions = require('./image-dimensions'), @@ -61,7 +61,7 @@ function getMetaData(data, root) { blog: { title: settingsCache.get('title'), description: settingsCache.get('description'), - url: urlService.utils.urlFor('home', true), + url: urlUtils.urlFor('home', true), facebook: settingsCache.get('facebook'), twitter: settingsCache.get('twitter'), timezone: settingsCache.get('active_timezone'), diff --git a/core/server/data/meta/og_image.js b/core/server/data/meta/og_image.js index f55db6372a..556187430e 100644 --- a/core/server/data/meta/og_image.js +++ b/core/server/data/meta/og_image.js @@ -1,4 +1,4 @@ -var urlService = require('../../services/url'), +var urlUtils = require('../../lib/url-utils'), getContextObject = require('./context_object.js'), _ = require('lodash'); @@ -8,9 +8,9 @@ function getOgImage(data) { if (_.includes(context, 'post') || _.includes(context, 'page') || _.includes(context, 'amp')) { if (contextObject.og_image) { - return urlService.utils.urlFor('image', {image: contextObject.og_image}, true); + return urlUtils.urlFor('image', {image: contextObject.og_image}, true); } else if (contextObject.feature_image) { - return urlService.utils.urlFor('image', {image: contextObject.feature_image}, true); + return urlUtils.urlFor('image', {image: contextObject.feature_image}, true); } } diff --git a/core/server/data/meta/paginated_url.js b/core/server/data/meta/paginated_url.js index f4233c5848..c468385d8e 100644 --- a/core/server/data/meta/paginated_url.js +++ b/core/server/data/meta/paginated_url.js @@ -1,5 +1,5 @@ var _ = require('lodash'), - urlService = require('../../services/url'); + urlUtils = require('../../lib/url-utils'); function getPaginatedUrl(page, data, absolute) { // If we don't have enough information, return null right away @@ -7,7 +7,7 @@ function getPaginatedUrl(page, data, absolute) { return null; } // routeKeywords.page: 'page' - var pagePath = urlService.utils.urlJoin('/page/'), + var pagePath = urlUtils.urlJoin('/page/'), // Try to match the base url, as whatever precedes the pagePath // routeKeywords.page: 'page' baseUrlPattern = new RegExp('(.+)?(/page/\\d+/)'), @@ -17,20 +17,20 @@ function getPaginatedUrl(page, data, absolute) { newRelativeUrl; if (page === 'next' && data.pagination.next) { - newRelativeUrl = urlService.utils.urlJoin(pagePath, data.pagination.next, '/'); + newRelativeUrl = urlUtils.urlJoin(pagePath, data.pagination.next, '/'); } else if (page === 'prev' && data.pagination.prev) { - newRelativeUrl = data.pagination.prev > 1 ? urlService.utils.urlJoin(pagePath, data.pagination.prev, '/') : '/'; + newRelativeUrl = data.pagination.prev > 1 ? urlUtils.urlJoin(pagePath, data.pagination.prev, '/') : '/'; } else if (_.isNumber(page)) { - newRelativeUrl = page > 1 ? urlService.utils.urlJoin(pagePath, page, '/') : '/'; + newRelativeUrl = page > 1 ? urlUtils.urlJoin(pagePath, page, '/') : '/'; } else { // If none of the cases match, return null right away return null; } // baseUrl can be undefined, if there was nothing preceding the pagePath (e.g. first page of the index channel) - newRelativeUrl = baseUrl ? urlService.utils.urlJoin(baseUrl, newRelativeUrl) : newRelativeUrl; + newRelativeUrl = baseUrl ? urlUtils.urlJoin(baseUrl, newRelativeUrl) : newRelativeUrl; - return urlService.utils.urlFor({relativeUrl: newRelativeUrl, secure: data.secure}, absolute); + return urlUtils.urlFor({relativeUrl: newRelativeUrl, secure: data.secure}, absolute); } module.exports = getPaginatedUrl; diff --git a/core/server/data/meta/twitter_image.js b/core/server/data/meta/twitter_image.js index 9c4b2e3808..1f838a416b 100644 --- a/core/server/data/meta/twitter_image.js +++ b/core/server/data/meta/twitter_image.js @@ -1,4 +1,4 @@ -var urlService = require('../../services/url'), +var urlUtils = require('../../lib/url-utils'), getContextObject = require('./context_object.js'), _ = require('lodash'); @@ -8,9 +8,9 @@ function getTwitterImage(data) { if (_.includes(context, 'post') || _.includes(context, 'page') || _.includes(context, 'amp')) { if (contextObject.twitter_image) { - return urlService.utils.urlFor('image', {image: contextObject.twitter_image}, true); + return urlUtils.urlFor('image', {image: contextObject.twitter_image}, true); } else if (contextObject.feature_image) { - return urlService.utils.urlFor('image', {image: contextObject.feature_image}, true); + return urlUtils.urlFor('image', {image: contextObject.feature_image}, true); } } diff --git a/core/server/data/meta/url.js b/core/server/data/meta/url.js index 0362e4d798..4338987364 100644 --- a/core/server/data/meta/url.js +++ b/core/server/data/meta/url.js @@ -1,4 +1,5 @@ var schema = require('../schema').checks, + urlUtils = require('../../lib/url-utils'), urlService = require('../../services/url'); // This cleans the url from any `/amp` postfixes, so we'll never @@ -26,9 +27,7 @@ function getUrl(data, absolute) { * A long term solution should be part of the final version of Dynamic Routing. */ if (data.status !== 'published' && urlService.getUrlByResourceId(data.id) === '/404/') { - return urlService - .utils - .urlFor({relativeUrl: urlService.utils.urlJoin('/p', data.uuid, '/'), secure: data.secure}, null, absolute); + return urlUtils.urlFor({relativeUrl: urlUtils.urlJoin('/p', data.uuid, '/'), secure: data.secure}, null, absolute); } return urlService.getUrlByResourceId(data.id, {secure: data.secure, absolute: absolute, withSubdirectory: true}); @@ -39,11 +38,11 @@ function getUrl(data, absolute) { } if (schema.isNav(data)) { - return urlService.utils.urlFor('nav', {nav: data, secure: data.secure}, absolute); + return urlUtils.urlFor('nav', {nav: data, secure: data.secure}, absolute); } // sanitize any trailing `/amp` in the url - return sanitizeAmpUrl(urlService.utils.urlFor(data, {}, absolute)); + return sanitizeAmpUrl(urlUtils.urlFor(data, {}, absolute)); } module.exports = getUrl; diff --git a/core/server/data/validation/index.js b/core/server/data/validation/index.js index f0751e4fc6..d3d35ac8d0 100644 --- a/core/server/data/validation/index.js +++ b/core/server/data/validation/index.js @@ -6,7 +6,7 @@ var schema = require('../schema').tables, Promise = require('bluebird'), common = require('../../lib/common'), settingsCache = require('../../services/settings/cache'), - urlService = require('../../services/url'), + urlUtils = require('../../lib/url-utils'), validatePassword, validateSchema, @@ -96,7 +96,7 @@ validator.extend('isSlug', function isSlug(str) { validatePassword = function validatePassword(password, email, blogTitle) { var validationResult = {isValid: true}, disallowedPasswords = ['password', 'ghost', 'passw0rd'], - blogUrl = urlService.utils.urlFor('home', true), + blogUrl = urlUtils.urlFor('home', true), badPasswords = [ '1234567890', 'qwertyuiop', diff --git a/core/server/data/xml/sitemap/base-generator.js b/core/server/data/xml/sitemap/base-generator.js index 98c0dc90ef..fcdbb53ace 100644 --- a/core/server/data/xml/sitemap/base-generator.js +++ b/core/server/data/xml/sitemap/base-generator.js @@ -2,7 +2,7 @@ const _ = require('lodash'), xml = require('xml'), moment = require('moment'), path = require('path'), - urlService = require('../../../services/url'), + urlUtils = require('../../../lib/url-utils'), localUtils = require('./utils'); // Sitemap specific xml namespace declarations that should not change @@ -112,7 +112,7 @@ class BaseSiteMapGenerator { } // Grab the image url - imageUrl = urlService.utils.urlFor('image', {image: image}, true); + imageUrl = urlUtils.urlFor('image', {image: image}, true); // Verify the url structure if (!this.validateImageUrl(imageUrl)) { diff --git a/core/server/data/xml/sitemap/index-generator.js b/core/server/data/xml/sitemap/index-generator.js index 16d50db178..bb87b6e308 100644 --- a/core/server/data/xml/sitemap/index-generator.js +++ b/core/server/data/xml/sitemap/index-generator.js @@ -1,7 +1,7 @@ const _ = require('lodash'), xml = require('xml'), moment = require('moment'), - urlService = require('../../../services/url'), + urlUtils = require('../../../lib/url-utils'), localUtils = require('./utils'); const XMLNS_DECLS = { @@ -29,7 +29,7 @@ class SiteMapIndexGenerator { generateSiteMapUrlElements() { return _.map(this.types, (resourceType) => { - var url = urlService.utils.urlFor({relativeUrl: '/sitemap-' + resourceType.name + '.xml'}, true), + var url = urlUtils.urlFor({relativeUrl: '/sitemap-' + resourceType.name + '.xml'}, true), lastModified = resourceType.lastModified; return { diff --git a/core/server/data/xml/sitemap/utils.js b/core/server/data/xml/sitemap/utils.js index 9a3eb7013b..2b46f78ef5 100644 --- a/core/server/data/xml/sitemap/utils.js +++ b/core/server/data/xml/sitemap/utils.js @@ -1,9 +1,9 @@ -var urlService = require('../../../services/url'), +var urlUtils = require('../../../lib/url-utils'), sitemapsUtils; sitemapsUtils = { getDeclarations: function () { - var baseUrl = urlService.utils.urlFor('sitemap_xsl', true); + var baseUrl = urlUtils.urlFor('sitemap_xsl', true); baseUrl = baseUrl.replace(/^(http:|https:)/, ''); return '' + ''; diff --git a/core/server/ghost-server.js b/core/server/ghost-server.js index 67be59ec08..682dd0f4f0 100644 --- a/core/server/ghost-server.js +++ b/core/server/ghost-server.js @@ -6,7 +6,7 @@ var debug = require('ghost-ignition').debug('server'), path = require('path'), _ = require('lodash'), config = require('./config'), - urlService = require('./services/url'), + urlUtils = require('./lib/url-utils'), common = require('./lib/common'), moment = require('moment'); @@ -193,7 +193,7 @@ GhostServer.prototype.logStartMessages = function () { // Startup & Shutdown messages if (config.get('env') === 'production') { common.logging.info(common.i18n.t('notices.httpServer.ghostIsRunningIn', {env: config.get('env')})); - common.logging.info(common.i18n.t('notices.httpServer.yourBlogIsAvailableOn', {url: urlService.utils.urlFor('home', true)})); + common.logging.info(common.i18n.t('notices.httpServer.yourBlogIsAvailableOn', {url: urlUtils.urlFor('home', true)})); common.logging.info(common.i18n.t('notices.httpServer.ctrlCToShutDown')); } else { common.logging.info(common.i18n.t('notices.httpServer.ghostIsRunningIn', {env: config.get('env')})); @@ -201,7 +201,7 @@ GhostServer.prototype.logStartMessages = function () { host: config.get('server').socket || config.get('server').host, port: config.get('server').port })); - common.logging.info(common.i18n.t('notices.httpServer.urlConfiguredAs', {url: urlService.utils.urlFor('home', true)})); + common.logging.info(common.i18n.t('notices.httpServer.urlConfiguredAs', {url: urlUtils.urlFor('home', true)})); common.logging.info(common.i18n.t('notices.httpServer.ctrlCToShutDown')); } diff --git a/core/server/helpers/img_url.js b/core/server/helpers/img_url.js index 2527fde7fc..d5156c555d 100644 --- a/core/server/helpers/img_url.js +++ b/core/server/helpers/img_url.js @@ -10,8 +10,8 @@ const url = require('url'); const _ = require('lodash'); const proxy = require('./proxy'); -const urlService = proxy.urlService; -const STATIC_IMAGE_URL_PREFIX = `/${urlService.utils.STATIC_IMAGE_URL_PREFIX}`; +const urlUtils = proxy.urlUtils; +const STATIC_IMAGE_URL_PREFIX = `/${urlUtils.STATIC_IMAGE_URL_PREFIX}`; module.exports = function imgUrl(requestedImageUrl, options) { // CASE: if no url is passed, e.g. `{{img_url}}` we show a warning @@ -47,11 +47,11 @@ module.exports = function imgUrl(requestedImageUrl, options) { } function getImageUrl(image) { - return urlService.utils.urlFor('image', {image}, absoluteUrlRequested); + return urlUtils.urlFor('image', {image}, absoluteUrlRequested); } function ensureRelativePath(image) { - return urlService.utils.absoluteToRelative(image); + return urlUtils.absoluteToRelative(image); } // CASE: only make paths relative if we didn't get a request for an absolute url @@ -81,7 +81,7 @@ function getImageSizeOptions(options) { } function detectInternalImage(requestedImageUrl) { - const siteUrl = urlService.utils.getBlogUrl(); + const siteUrl = urlUtils.getBlogUrl(); const isAbsoluteImage = /https?:\/\//.test(requestedImageUrl); const isAbsoluteInternalImage = isAbsoluteImage && requestedImageUrl.startsWith(siteUrl); diff --git a/core/server/helpers/proxy.js b/core/server/helpers/proxy.js index 59003c58f2..93f76552fe 100644 --- a/core/server/helpers/proxy.js +++ b/core/server/helpers/proxy.js @@ -60,5 +60,6 @@ module.exports = { socialUrls: require('@tryghost/social-urls'), blogIcon: require('../lib/image/blog-icon'), urlService: require('../services/url'), + urlUtils: require('../lib/url-utils'), localUtils: require('./utils') }; diff --git a/core/server/index.js b/core/server/index.js index 02fe1355b3..3233bf9e75 100644 --- a/core/server/index.js +++ b/core/server/index.js @@ -14,7 +14,7 @@ const Promise = require('bluebird'); const config = require('./config'); const common = require('./lib/common'); const migrator = require('./data/db/migrator'); -const urlService = require('./services/url'); +const urlUtils = require('./lib/url-utils'); let parentApp; function initialiseServices() { @@ -46,7 +46,7 @@ function initialiseServices() { active: config.get('scheduling').active, // NOTE: When changing API version need to consider how to migrate custom scheduling adapters // that rely on URL to lookup persisted scheduled records (jobs, etc.). Ref: https://github.com/TryGhost/Ghost/pull/10726#issuecomment-489557162 - apiUrl: urlService.utils.urlFor('api', {version: 'v0.1', versionType: 'content'}, true), + apiUrl: urlUtils.urlFor('api', {version: 'v0.1', versionType: 'content'}, true), internalPath: config.get('paths').internalSchedulingPath, contentPath: config.getContentPath('scheduling') }) diff --git a/core/server/lib/image/blog-icon.js b/core/server/lib/image/blog-icon.js index 280ba47aab..9116f624d1 100644 --- a/core/server/lib/image/blog-icon.js +++ b/core/server/lib/image/blog-icon.js @@ -4,8 +4,8 @@ var sizeOf = require('image-size'), path = require('path'), config = require('../../config'), common = require('../common'), + urlUtils = require('../../lib/url-utils'), settingsCache = require('../../services/settings/cache'), - urlService = require('../../services/url'), storageUtils = require('../../adapters/storage/utils'), getIconDimensions, isIcoImageType, @@ -89,15 +89,15 @@ getIconUrl = function getIconUrl(absolut) { if (absolut) { if (blogIcon) { - return isIcoImageType(blogIcon) ? urlService.utils.urlFor({relativeUrl: '/favicon.ico'}, true) : urlService.utils.urlFor({relativeUrl: '/favicon.png'}, true); + return isIcoImageType(blogIcon) ? urlUtils.urlFor({relativeUrl: '/favicon.ico'}, true) : urlUtils.urlFor({relativeUrl: '/favicon.png'}, true); } else { - return urlService.utils.urlFor({relativeUrl: '/favicon.ico'}, true); + return urlUtils.urlFor({relativeUrl: '/favicon.ico'}, true); } } else { if (blogIcon) { - return isIcoImageType(blogIcon) ? urlService.utils.urlFor({relativeUrl: '/favicon.ico'}) : urlService.utils.urlFor({relativeUrl: '/favicon.png'}); + return isIcoImageType(blogIcon) ? urlUtils.urlFor({relativeUrl: '/favicon.ico'}) : urlUtils.urlFor({relativeUrl: '/favicon.png'}); } else { - return urlService.utils.urlFor({relativeUrl: '/favicon.ico'}); + return urlUtils.urlFor({relativeUrl: '/favicon.ico'}); } } }; diff --git a/core/server/lib/image/image-size.js b/core/server/lib/image/image-size.js index 83a8c093e7..ea40643e9d 100644 --- a/core/server/lib/image/image-size.js +++ b/core/server/lib/image/image-size.js @@ -5,7 +5,7 @@ const url = require('url'); const Promise = require('bluebird'); const _ = require('lodash'); const request = require('../request'); -const urlService = require('../../services/url'); +const urlUtils = require('../../lib/url-utils'); const common = require('../common'); const config = require('../../config'); const storage = require('../../adapters/storage'); @@ -133,7 +133,7 @@ const getImageSizeFromUrl = (imagePath) => { // CASE: pre 1.0 users were able to use an asset path for their blog logo if (imagePath.match(/^\/assets/)) { - imagePath = urlService.utils.urlJoin(urlService.utils.urlFor('home', true), urlService.utils.getSubdir(), '/', imagePath); + imagePath = urlUtils.urlJoin(urlUtils.urlFor('home', true), urlUtils.getSubdir(), '/', imagePath); } debug('requested imagePath:', imagePath); @@ -202,7 +202,7 @@ const getImageSizeFromUrl = (imagePath) => { const getImageSizeFromStoragePath = (imagePath) => { let filePath; - imagePath = urlService.utils.urlFor('image', {image: imagePath}, true); + imagePath = urlUtils.urlFor('image', {image: imagePath}, true); // get the storage readable filePath filePath = storageUtils.getLocalFileStoragePath(imagePath); diff --git a/core/server/lib/url-utils/index.js b/core/server/lib/url-utils/index.js new file mode 100644 index 0000000000..372f103a40 --- /dev/null +++ b/core/server/lib/url-utils/index.js @@ -0,0 +1,13 @@ +const UrlUtils = require('@tryghost/url-utils'); +const config = require('../../config'); + +const urlUtils = new UrlUtils({ + url: config.get('url'), + adminUrl: config.get('admin:url'), + apiVersions: config.get('api:versions'), + slugs: config.get('slugs').protected, + redirectCacheMaxAge: config.get('caching:301:maxAge'), + baseApiPath: '/ghost/api' +}); + +module.exports = urlUtils; diff --git a/core/server/models/base/index.js b/core/server/models/base/index.js index 90c278eb33..3570c115b8 100644 --- a/core/server/models/base/index.js +++ b/core/server/models/base/index.js @@ -16,7 +16,7 @@ const _ = require('lodash'), common = require('../../lib/common'), security = require('../../lib/security'), schema = require('../../data/schema'), - urlService = require('../../services/url'), + urlUtils = require('../../lib/url-utils'), validation = require('../../data/validation'), plugins = require('../plugins'); @@ -1110,7 +1110,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({ } // Some keywords cannot be changed - const slugList = _.union(config.get('slugs').reserved, urlService.utils.getProtectedSlugs()); + const slugList = _.union(config.get('slugs').reserved, urlUtils.getProtectedSlugs()); slug = _.includes(slugList, slug) ? slug + '-' + baseName : slug; // if slug is empty after trimming use the model name diff --git a/core/server/services/auth/session/middleware.js b/core/server/services/auth/session/middleware.js index e702122d37..2ff99bb9a0 100644 --- a/core/server/services/auth/session/middleware.js +++ b/core/server/services/auth/session/middleware.js @@ -6,7 +6,7 @@ const config = require('../../../config'); const settingsCache = require('../../settings/cache'); const models = require('../../../models'); const SessionStore = require('./store'); -const urlService = require('../../url'); +const urlUtils = require('../../../lib/url-utils'); const getOrigin = (req) => { const origin = req.get('origin'); @@ -39,9 +39,9 @@ const getSession = (req, res, next) => { cookie: { maxAge: constants.SIX_MONTH_MS, httpOnly: true, - path: urlService.utils.getSubdir() + '/ghost', + path: urlUtils.getSubdir() + '/ghost', sameSite: 'lax', - secure: urlService.utils.isSSL(config.get('url')) + secure: urlUtils.isSSL(config.get('url')) } }); } diff --git a/core/server/services/mail/GhostMailer.js b/core/server/services/mail/GhostMailer.js index 9581b893d7..bbd2718613 100644 --- a/core/server/services/mail/GhostMailer.js +++ b/core/server/services/mail/GhostMailer.js @@ -6,7 +6,7 @@ var _ = require('lodash'), config = require('../../config'), common = require('../../lib/common'), settingsCache = require('../settings/cache'), - urlService = require('../url'); + urlUtils = require('../../lib/url-utils'); function GhostMailer() { var nodemailer = require('nodemailer'), @@ -39,7 +39,7 @@ GhostMailer.prototype.from = function () { // Moved it to its own module GhostMailer.prototype.getDomain = function () { - var domain = urlService.utils.urlFor('home', true).match(new RegExp('^https?://([^/:?#]+)(?:[/:?#]|$)', 'i')); + var domain = urlUtils.urlFor('home', true).match(new RegExp('^https?://([^/:?#]+)(?:[/:?#]|$)', 'i')); return domain && domain[1]; }; diff --git a/core/server/services/mail/utils.js b/core/server/services/mail/utils.js index e0b2887f63..6b627fe813 100644 --- a/core/server/services/mail/utils.js +++ b/core/server/services/mail/utils.js @@ -2,7 +2,7 @@ var _ = require('lodash').runInContext(), fs = require('fs-extra'), path = require('path'), htmlToText = require('html-to-text'), - urlService = require('../url'), + urlUtils = require('../../lib/url-utils'), templatesDir = path.resolve(__dirname, '..', 'mail', 'templates'); _.templateSettings.interpolate = /{{([\s\S]+?)}}/g; @@ -12,7 +12,7 @@ exports.generateContent = function generateContent(options) { data; defaults = { - siteUrl: urlService.utils.urlFor('home', true) + siteUrl: urlUtils.urlFor('home', true) }; data = _.defaults(defaults, options.data); diff --git a/core/server/services/members/api.js b/core/server/services/members/api.js index a8b9069f4a..99721fce41 100644 --- a/core/server/services/members/api.js +++ b/core/server/services/members/api.js @@ -1,6 +1,6 @@ const url = require('url'); const settingsCache = require('../settings/cache'); -const urlService = require('../url'); +const urlUtils = require('../../lib/url-utils'); const MembersApi = require('@tryghost/members-api'); const MembersSSR = require('@tryghost/members-ssr'); const common = require('../../lib/common'); @@ -110,7 +110,7 @@ function getSubscriptionSettings() { return membersSettings; } -const siteUrl = urlService.utils.getSiteUrl(); +const siteUrl = urlUtils.getSiteUrl(); const siteOrigin = doBlock(() => { const {protocol, host} = url.parse(siteUrl); return `${protocol}//${host}`; @@ -118,7 +118,7 @@ const siteOrigin = doBlock(() => { const getApiUrl = ({version, type}) => { const {href} = new url.URL( - urlService.utils.getApiPath({version, type}), + urlUtils.getApiPath({version, type}), siteUrl ); return href; @@ -217,7 +217,7 @@ common.events.on('settings.edited', updateSettingFromModel); module.exports = membersApiInstance; module.exports.ssr = MembersSSR({ - cookieSecure: urlService.utils.isSSL(siteUrl), + cookieSecure: urlUtils.isSSL(siteUrl), cookieKeys: [settingsCache.get('theme_session_secret')], membersApi: membersApiInstance }); diff --git a/core/server/services/routing/CollectionRouter.js b/core/server/services/routing/CollectionRouter.js index c679363622..65609f1be2 100644 --- a/core/server/services/routing/CollectionRouter.js +++ b/core/server/services/routing/CollectionRouter.js @@ -1,6 +1,6 @@ const debug = require('ghost-ignition').debug('services:routing:collection-router'); const common = require('../../lib/common'); -const urlService = require('../url'); +const urlUtils = require('../../lib/url-utils'); const ParentRouter = require('./ParentRouter'); const controllers = require('./controllers'); @@ -46,7 +46,7 @@ class CollectionRouter extends ParentRouter { // @NOTE: url options are only required when registering urls in express. // e.g. the UrlService will access the routes and doesn't want to know about possible url options if (options.withUrlOptions) { - return urlService.utils.urlJoin(this.permalinks.value, '/:options(edit)?/'); + return urlUtils.urlJoin(this.permalinks.value, '/:options(edit)?/'); } return this.permalinks.value; @@ -73,7 +73,7 @@ class CollectionRouter extends ParentRouter { // REGISTER: enable pagination by default this.router().param('page', middlewares.pageParam); - this.mountRoute(urlService.utils.urlJoin(this.route.value, 'page', ':page(\\d+)'), controllers.collection); + this.mountRoute(urlUtils.urlJoin(this.route.value, 'page', ':page(\\d+)'), controllers.collection); // REGISTER: is rss enabled? if (this.rss) { @@ -179,7 +179,7 @@ class CollectionRouter extends ParentRouter { getRoute(options) { options = options || {}; - return urlService.utils.createUrl(this.route.value, options.absolute, options.secure); + return urlUtils.createUrl(this.route.value, options.absolute, options.secure); } /** @@ -192,7 +192,7 @@ class CollectionRouter extends ParentRouter { return null; } - return urlService.utils.createUrl(urlService.utils.urlJoin(this.route.value, this.rssRouter.route.value), options.absolute, options.secure); + return urlUtils.createUrl(urlUtils.urlJoin(this.route.value, this.rssRouter.route.value), options.absolute, options.secure); } reset() { diff --git a/core/server/services/routing/ParentRouter.js b/core/server/services/routing/ParentRouter.js index 8d330d9500..a7b3f342f2 100644 --- a/core/server/services/routing/ParentRouter.js +++ b/core/server/services/routing/ParentRouter.js @@ -13,7 +13,7 @@ const debug = require('ghost-ignition').debug('services:routing:ParentRouter'), _ = require('lodash'), url = require('url'), security = require('../../lib/security'), - urlService = require('../url'), + urlUtils = require('../../lib/url-utils'), registry = require('./registry'); /** @@ -107,8 +107,8 @@ class ParentRouter extends EventEmitter { const matchPath = this.permalinks.getValue().replace(/:\w+/g, '[a-zA-Z0-9-_]+'); const toAppend = req.url.replace(new RegExp(matchPath), ''); - return urlService.utils.redirect301(res, url.format({ - pathname: urlService.utils.createUrl(urlService.utils.urlJoin(targetRoute, toAppend), false, false, true), + return urlUtils.redirect301(res, url.format({ + pathname: urlUtils.createUrl(urlUtils.urlJoin(targetRoute, toAppend), false, false, true), search: url.parse(req.originalUrl).search })); } @@ -201,7 +201,7 @@ class ParentRouter extends EventEmitter { getRoute(options) { options = options || {}; - return urlService.utils.createUrl(this.route.value, options.absolute, options.secure); + return urlUtils.createUrl(this.route.value, options.absolute, options.secure); } /** diff --git a/core/server/services/routing/PreviewRouter.js b/core/server/services/routing/PreviewRouter.js index 91ab22ffad..8c9d40f3ca 100644 --- a/core/server/services/routing/PreviewRouter.js +++ b/core/server/services/routing/PreviewRouter.js @@ -1,5 +1,5 @@ const ParentRouter = require('./ParentRouter'); -const urlService = require('../url'); +const urlUtils = require('../../lib/url-utils'); const controllers = require('./controllers'); /** @@ -26,7 +26,7 @@ class PreviewRouter extends ParentRouter { this.router().use(this._prepareContext.bind(this)); // REGISTER: actual preview route - this.mountRoute(urlService.utils.urlJoin(this.route.value, ':uuid', ':options?'), controllers.preview); + this.mountRoute(urlUtils.urlJoin(this.route.value, ':uuid', ':options?'), controllers.preview); } /** diff --git a/core/server/services/routing/RSSRouter.js b/core/server/services/routing/RSSRouter.js index 034d3bdf0a..02b73b6eee 100644 --- a/core/server/services/routing/RSSRouter.js +++ b/core/server/services/routing/RSSRouter.js @@ -1,5 +1,5 @@ const ParentRouter = require('./ParentRouter'); -const urlService = require('../url'); +const urlUtils = require('../../lib/url-utils'); const controllers = require('./controllers'); const middlewares = require('./middlewares'); @@ -28,7 +28,7 @@ class RSSRouter extends ParentRouter { this.router().param('page', middlewares.pageParam); // REGISTER: actual rss route - this.mountRoute(urlService.utils.urlJoin(this.route.value, ':page(\\d+)'), controllers.rss); + this.mountRoute(urlUtils.urlJoin(this.route.value, ':page(\\d+)'), controllers.rss); // REGISTER: redirect rule this.mountRoute('/feed/', this._redirectFeedRequest.bind(this)); @@ -41,11 +41,10 @@ class RSSRouter extends ParentRouter { * @private */ _redirectFeedRequest(req, res) { - urlService - .utils + urlUtils .redirect301( res, - urlService.utils.urlJoin(urlService.utils.getSubdir(), req.baseUrl, this.route.value) + urlUtils.urlJoin(urlUtils.getSubdir(), req.baseUrl, this.route.value) ); } } diff --git a/core/server/services/routing/StaticPagesRouter.js b/core/server/services/routing/StaticPagesRouter.js index 7e8108735f..3f326f43e8 100644 --- a/core/server/services/routing/StaticPagesRouter.js +++ b/core/server/services/routing/StaticPagesRouter.js @@ -1,5 +1,5 @@ const debug = require('ghost-ignition').debug('services:routing:static-pages-router'); -const urlService = require('../url'); +const urlUtils = require('../../lib/url-utils'); const ParentRouter = require('./ParentRouter'); const controllers = require('./controllers'); const common = require('../../lib/common'); @@ -24,7 +24,7 @@ class StaticPagesRouter extends ParentRouter { // @NOTE: url options are only required when registering urls in express. // e.g. the UrlService will access the routes and doesn't want to know about possible url options if (options.withUrlOptions) { - return urlService.utils.urlJoin(this.permalinks.value, '/:options(edit)?/'); + return urlUtils.urlJoin(this.permalinks.value, '/:options(edit)?/'); } return this.permalinks.value; diff --git a/core/server/services/routing/StaticRoutesRouter.js b/core/server/services/routing/StaticRoutesRouter.js index d00e56a3ca..e0afc6ff1e 100644 --- a/core/server/services/routing/StaticRoutesRouter.js +++ b/core/server/services/routing/StaticRoutesRouter.js @@ -1,6 +1,6 @@ const debug = require('ghost-ignition').debug('services:routing:static-routes-router'); const common = require('../../lib/common'); -const urlService = require('../../services/url'); +const urlUtils = require('../../lib/url-utils'); const RSSRouter = require('./RSSRouter'); const controllers = require('./controllers'); const middlewares = require('./middlewares'); @@ -59,7 +59,7 @@ class StaticRoutesRouter extends ParentRouter { // REGISTER: pagination this.router().param('page', middlewares.pageParam); - this.mountRoute(urlService.utils.urlJoin(this.route.value, 'page', ':page(\\d+)'), controllers[this.controller]); + this.mountRoute(urlUtils.urlJoin(this.route.value, 'page', ':page(\\d+)'), controllers[this.controller]); common.events.emit('router.created', this); } diff --git a/core/server/services/routing/TaxonomyRouter.js b/core/server/services/routing/TaxonomyRouter.js index 5103e80de1..1b3209d641 100644 --- a/core/server/services/routing/TaxonomyRouter.js +++ b/core/server/services/routing/TaxonomyRouter.js @@ -2,7 +2,7 @@ const debug = require('ghost-ignition').debug('services:routing:taxonomy-router' const common = require('../../lib/common'); const ParentRouter = require('./ParentRouter'); const RSSRouter = require('./RSSRouter'); -const urlService = require('../url'); +const urlUtils = require('../../lib/url-utils'); const controllers = require('./controllers'); const middlewares = require('./middlewares'); @@ -50,10 +50,10 @@ class TaxonomyRouter extends ParentRouter { // REGISTER: enable pagination for each taxonomy by default this.router().param('page', middlewares.pageParam); - this.mountRoute(urlService.utils.urlJoin(this.permalinks.value, 'page', ':page(\\d+)'), controllers.channel); + this.mountRoute(urlUtils.urlJoin(this.permalinks.value, 'page', ':page(\\d+)'), controllers.channel); // REGISTER: edit redirect to admin client e.g. /tag/:slug/edit - this.mountRoute(urlService.utils.urlJoin(this.permalinks.value, 'edit'), this._redirectEditOption.bind(this)); + this.mountRoute(urlUtils.urlJoin(this.permalinks.value, 'edit'), this._redirectEditOption.bind(this)); common.events.emit('router.created', this); } @@ -88,7 +88,7 @@ class TaxonomyRouter extends ParentRouter { * @private */ _redirectEditOption(req, res) { - urlService.utils.redirectToAdmin(302, res, this.RESOURCE_CONFIG.TAXONOMIES[this.taxonomyKey].editRedirect.replace(':slug', req.params.slug)); + urlUtils.redirectToAdmin(302, res, this.RESOURCE_CONFIG.TAXONOMIES[this.taxonomyKey].editRedirect.replace(':slug', req.params.slug)); } /** diff --git a/core/server/services/routing/controllers/entry.js b/core/server/services/routing/controllers/entry.js index 6ac7875c4f..c3c39d5284 100644 --- a/core/server/services/routing/controllers/entry.js +++ b/core/server/services/routing/controllers/entry.js @@ -1,6 +1,7 @@ const debug = require('ghost-ignition').debug('services:routing:controllers:entry'), url = require('url'), urlService = require('../../url'), + urlUtils = require('../../../lib/url-utils'), helpers = require('../helpers'); /** @@ -34,7 +35,7 @@ module.exports = function entryController(req, res, next) { debug('redirect. is edit url'); const resourceType = entry.page ? 'page' : 'post'; - return urlService.utils.redirectToAdmin(302, res, `/editor/${resourceType}/${entry.id}`); + return urlUtils.redirectToAdmin(302, res, `/editor/${resourceType}/${entry.id}`); } /** @@ -74,10 +75,10 @@ module.exports = function entryController(req, res, next) { * @TODO: * Simplify if we drop v0.1. */ - if (urlService.utils.absoluteToRelative(entry.url, {withoutSubdirectory: true}) !== req.path) { + if (urlUtils.absoluteToRelative(entry.url, {withoutSubdirectory: true}) !== req.path) { debug('redirect'); - return urlService.utils.redirect301(res, url.format({ + return urlUtils.redirect301(res, url.format({ pathname: url.parse(entry.url).pathname, search: url.parse(req.originalUrl).search })); diff --git a/core/server/services/routing/controllers/preview.js b/core/server/services/routing/controllers/preview.js index ebab3c7ac6..9e81f0fd05 100644 --- a/core/server/services/routing/controllers/preview.js +++ b/core/server/services/routing/controllers/preview.js @@ -1,5 +1,6 @@ const debug = require('ghost-ignition').debug('services:routing:controllers:preview'), urlService = require('../../url'), + urlUtils = require('../../../lib/url-utils'), helpers = require('../helpers'); /** @@ -37,14 +38,14 @@ module.exports = function previewController(req, res, next) { const resourceType = post.page ? 'page' : 'post'; // CASE: last param of the url is /edit, redirect to admin - return urlService.utils.redirectToAdmin(302, res, `/editor/${resourceType}/${post.id}`); + return urlUtils.redirectToAdmin(302, res, `/editor/${resourceType}/${post.id}`); } else if (req.params.options) { // CASE: unknown options param detected, ignore return next(); } if (post.status === 'published') { - return urlService.utils.redirect301(res, urlService.getUrlByResourceId(post.id, {withSubdirectory: true})); + return urlUtils.redirect301(res, urlService.getUrlByResourceId(post.id, {withSubdirectory: true})); } // @TODO: See helpers/secure diff --git a/core/server/services/routing/middlewares/page-param.js b/core/server/services/routing/middlewares/page-param.js index 38e2ad03c1..201f1740ae 100644 --- a/core/server/services/routing/middlewares/page-param.js +++ b/core/server/services/routing/middlewares/page-param.js @@ -1,5 +1,5 @@ const common = require('../../../lib/common'), - urlService = require('../../url'); + urlUtils = require('../../../lib/url-utils'); /** * @description Middleware, which validates and interprets the page param e.g. /page/1 @@ -20,9 +20,9 @@ module.exports = function handlePageParam(req, res, next, page) { // CASE: page 1 is an alias for the collection index, do a permanent 301 redirect // @TODO: this belongs into the rss router! if (rssRegex.test(req.url)) { - return urlService.utils.redirect301(res, req.originalUrl.replace(rssRegex, '/rss/')); + return urlUtils.redirect301(res, req.originalUrl.replace(rssRegex, '/rss/')); } else { - return urlService.utils.redirect301(res, req.originalUrl.replace(pageRegex, '/')); + return urlUtils.redirect301(res, req.originalUrl.replace(pageRegex, '/')); } } else if (page < 1 || isNaN(page)) { return next(new common.errors.NotFoundError({ diff --git a/core/server/services/rss/generate-feed.js b/core/server/services/rss/generate-feed.js index 1f0f057a2b..4695272f6b 100644 --- a/core/server/services/rss/generate-feed.js +++ b/core/server/services/rss/generate-feed.js @@ -1,6 +1,7 @@ var downsize = require('downsize'), Promise = require('bluebird'), RSS = require('rss'), + urlUtils = require('../../lib/url-utils'), urlService = require('../../services/url'), generateFeed, generateItem, @@ -21,7 +22,7 @@ generateTags = function generateTags(data) { generateItem = function generateItem(post, siteUrl, secure) { var itemUrl = urlService.getUrlByResourceId(post.id, {secure: secure, absolute: true}), - htmlContent = urlService.utils.makeAbsoluteUrls(post.html, siteUrl, itemUrl), + htmlContent = urlUtils.makeAbsoluteUrls(post.html, siteUrl, itemUrl), item = { title: post.title, // @TODO: DRY this up with data/meta/index & other excerpt code @@ -36,7 +37,7 @@ generateItem = function generateItem(post, siteUrl, secure) { imageUrl; if (post.feature_image) { - imageUrl = urlService.utils.urlFor('image', {image: post.feature_image, secure: secure}, true); + imageUrl = urlUtils.urlFor('image', {image: post.feature_image, secure: secure}, true); // Add a media content tag item.custom_elements.push({ @@ -71,14 +72,14 @@ generateItem = function generateItem(post, siteUrl, secure) { * @param {{title, description, safeVersion, secure, posts}} data */ generateFeed = function generateFeed(baseUrl, data) { - const siteUrl = urlService.utils.urlFor('home', {secure: data.secure}, true); + const siteUrl = urlUtils.urlFor('home', {secure: data.secure}, true); const feed = new RSS({ title: data.title, description: data.description, generator: 'Ghost ' + data.safeVersion, - feed_url: urlService.utils.urlFor({relativeUrl: baseUrl, secure: data.secure}, true), + feed_url: urlUtils.urlFor({relativeUrl: baseUrl, secure: data.secure}, true), site_url: siteUrl, - image_url: urlService.utils.urlFor({relativeUrl: 'favicon.png'}, true), + image_url: urlUtils.urlFor({relativeUrl: 'favicon.png'}, true), ttl: '60', custom_namespaces: { content: 'http://purl.org/rss/1.0/modules/content/', diff --git a/core/server/services/slack.js b/core/server/services/slack.js index 0e89973c70..fd1dcc4674 100644 --- a/core/server/services/slack.js +++ b/core/server/services/slack.js @@ -1,6 +1,7 @@ var common = require('../lib/common'), request = require('../lib/request'), imageLib = require('../lib/image'), + urlUtils = require('../lib/url-utils'), urlService = require('../services/url'), settingsCache = require('./settings/cache'), schema = require('../data/schema').checks, @@ -71,7 +72,7 @@ function ping(post) { title: title, title_link: message, author_name: blogTitle, - image_url: post ? urlService.utils.urlFor('image', {image: post.feature_image}, true) : null, + image_url: post ? urlUtils.urlFor('image', {image: post.feature_image}, true) : null, color: '#008952', fields: [ { @@ -84,7 +85,7 @@ function ping(post) { { fallback: 'Sorry, content cannot be shown.', color: '#008952', - thumb_url: author ? urlService.utils.urlFor('image', {image: author.profile_image}, true) : null, + thumb_url: author ? urlUtils.urlFor('image', {image: author.profile_image}, true) : null, fields: [ { title: 'Author', diff --git a/core/server/services/themes/middleware.js b/core/server/services/themes/middleware.js index efda48c570..fe0e7a370e 100644 --- a/core/server/services/themes/middleware.js +++ b/core/server/services/themes/middleware.js @@ -1,6 +1,6 @@ const _ = require('lodash'); const hbs = require('./engine'); -const urlService = require('../url'); +const urlUtils = require('../../lib/url-utils'); const config = require('../../config'); const common = require('../../lib/common'); const settingsCache = require('../settings/cache'); @@ -75,7 +75,7 @@ function updateLocalTemplateData(req, res, next) { function updateLocalTemplateOptions(req, res, next) { const localTemplateOptions = hbs.getLocalTemplateOptions(res.locals); const siteData = { - url: urlService.utils.urlFor('home', {secure: req.secure, trailingSlash: false}, true) + url: urlUtils.urlFor('home', {secure: req.secure, trailingSlash: false}, true) }; hbs.updateLocalTemplateOptions(res.locals, _.merge({}, localTemplateOptions, { diff --git a/core/server/services/url/UrlGenerator.js b/core/server/services/url/UrlGenerator.js index 7a58f353dc..11f3cebc09 100644 --- a/core/server/services/url/UrlGenerator.js +++ b/core/server/services/url/UrlGenerator.js @@ -1,7 +1,7 @@ const _ = require('lodash'), nql = require('@nexes/nql'), debug = require('ghost-ignition').debug('services:url:generator'), - localUtils = require('./utils'), + localUtils = require('../../lib/url-utils'), // @TODO: merge with filter plugin EXPANSIONS = [{ key: 'author', diff --git a/core/server/services/url/UrlService.js b/core/server/services/url/UrlService.js index 320c374c12..1a0ae0020c 100644 --- a/core/server/services/url/UrlService.js +++ b/core/server/services/url/UrlService.js @@ -6,7 +6,7 @@ const _debug = require('ghost-ignition').debug._base, Queue = require('./Queue'), Urls = require('./Urls'), Resources = require('./Resources'), - localUtils = require('./utils'); + localUtils = require('../../lib/url-utils'); /** * The url service class holds all instances in a centralised place. diff --git a/core/server/services/url/Urls.js b/core/server/services/url/Urls.js index 02d91dbd7a..20fc63c4df 100644 --- a/core/server/services/url/Urls.js +++ b/core/server/services/url/Urls.js @@ -1,6 +1,6 @@ const _ = require('lodash'); const debug = require('ghost-ignition').debug('services:url:urls'); -const localUtils = require('./utils'); +const localUtils = require('../../lib/url-utils'); const common = require('../../lib/common'); /** diff --git a/core/server/services/url/utils.js b/core/server/services/url/utils.js deleted file mode 100644 index d51377a1cd..0000000000 --- a/core/server/services/url/utils.js +++ /dev/null @@ -1,503 +0,0 @@ -// Contains all path information to be used throughout the codebase. -// Assumes that config.url is set, and is valid -const moment = require('moment-timezone'), - _ = require('lodash'), - url = require('url'), - cheerio = require('cheerio'), - config = require('../../config'), - settingsCache = require('../settings/cache'), - BASE_API_PATH = '/ghost/api', - STATIC_IMAGE_URL_PREFIX = 'content/images'; - -/** - * Returns API path combining base path and path for specific version asked or deprecated by default - * @param {Object} options {version} for which to get the path(stable, actice, deprecated), - * {type} admin|content: defaults to {version: deprecated, type: content} - * @return {string} API Path for version - */ -function getApiPath(options) { - const versionPath = getVersionPath(options); - return `${BASE_API_PATH}${versionPath}`; -} - -/** - * Returns path containing only the path for the specific version asked or deprecated by default - * @param {Object} options {version} for which to get the path(stable, actice, deprecated), - * {type} admin|content: defaults to {version: deprecated, type: content} - * @return {string} API version path - */ -function getVersionPath(options) { - const apiVersions = config.get('api:versions'); - let requestedVersion = options.version || 'v0.1'; - let requestedVersionType = options.type || 'content'; - let versionData = apiVersions[requestedVersion]; - if (typeof versionData === 'string') { - versionData = apiVersions[versionData]; - } - let versionPath = versionData[requestedVersionType]; - return `/${versionPath}/`; -} - -/** - * Returns the base URL of the blog as set in the config. - * - * Secure: - * If the request is secure, we want to force returning the blog url as https. - * Imagine Ghost runs with http, but nginx allows SSL connections. - * - * @param {boolean} secure - * @return {string} URL returns the url as defined in config, but always with a trailing `/` - */ -function getBlogUrl(secure) { - var blogUrl; - - if (secure) { - blogUrl = config.get('url').replace('http://', 'https://'); - } else { - blogUrl = config.get('url'); - } - - if (!blogUrl.match(/\/$/)) { - blogUrl += '/'; - } - - return blogUrl; -} - -/** - * Returns a subdirectory URL, if defined so in the config. - * @return {string} URL a subdirectory if configured. - */ -function getSubdir() { - // Parse local path location - var localPath = url.parse(config.get('url')).path, - subdir; - - // Remove trailing slash - if (localPath !== '/') { - localPath = localPath.replace(/\/$/, ''); - } - - subdir = localPath === '/' ? '' : localPath; - return subdir; -} - -function deduplicateSubDir(url) { - var subDir = getSubdir(), - subDirRegex; - - if (!subDir) { - return url; - } - - subDir = subDir.replace(/^\/|\/+$/, ''); - // we can have subdirs that match TLDs so we need to restrict matches to - // duplicates that start with a / or the beginning of the url - subDirRegex = new RegExp('(^|/)' + subDir + '/' + subDir + '/'); - - return url.replace(subDirRegex, '$1' + subDir + '/'); -} - -function getProtectedSlugs() { - var subDir = getSubdir(); - - if (!_.isEmpty(subDir)) { - return config.get('slugs').protected.concat([subDir.split('/').pop()]); - } else { - return config.get('slugs').protected; - } -} - -/** urlJoin - * Returns a URL/path for internal use in Ghost. - * @param {string} arguments takes arguments and concats those to a valid path/URL. - * @return {string} URL concatinated URL/path of arguments. - */ -function urlJoin() { - var args = Array.prototype.slice.call(arguments), - prefixDoubleSlash = false, - url; - - // Remove empty item at the beginning - if (args[0] === '') { - args.shift(); - } - - // Handle schemeless protocols - if (args[0].indexOf('//') === 0) { - prefixDoubleSlash = true; - } - - // join the elements using a slash - url = args.join('/'); - - // Fix multiple slashes - url = url.replace(/(^|[^:])\/\/+/g, '$1/'); - - // Put the double slash back at the beginning if this was a schemeless protocol - if (prefixDoubleSlash) { - url = url.replace(/^\//, '//'); - } - - url = deduplicateSubDir(url); - return url; -} - -/** - * admin:url is optional - */ -function getAdminUrl() { - var adminUrl = config.get('admin:url'), - subDir = getSubdir(); - - if (!adminUrl) { - return; - } - - if (!adminUrl.match(/\/$/)) { - adminUrl += '/'; - } - - adminUrl = urlJoin(adminUrl, subDir, '/'); - adminUrl = deduplicateSubDir(adminUrl); - return adminUrl; -} - -// ## createUrl -// Simple url creation from a given path -// Ensures that our urls contain the subdirectory if there is one -// And are correctly formatted as either relative or absolute -// Usage: -// createUrl('/', true) -> http://my-ghost-blog.com/ -// E.g. /blog/ subdir -// createUrl('/welcome-to-ghost/') -> /blog/welcome-to-ghost/ -// Parameters: -// - urlPath - string which must start and end with a slash -// - absolute (optional, default:false) - boolean whether or not the url should be absolute -// - secure (optional, default:false) - boolean whether or not to force SSL -// Returns: -// - a URL which always ends with a slash -function createUrl(urlPath, absolute, secure, trailingSlash) { - urlPath = urlPath || '/'; - absolute = absolute || false; - var base; - - // create base of url, always ends without a slash - if (absolute) { - base = getBlogUrl(secure); - } else { - base = getSubdir(); - } - - if (trailingSlash) { - if (!urlPath.match(/\/$/)) { - urlPath += '/'; - } - } - - return urlJoin(base, urlPath); -} - -/** - * creates the url path for a post based on blog timezone and permalink pattern - */ -function replacePermalink(permalink, resource) { - let output = permalink, - primaryTagFallback = 'all', - publishedAtMoment = moment.tz(resource.published_at || Date.now(), settingsCache.get('active_timezone')), - permalinkLookUp = { - year: function () { - return publishedAtMoment.format('YYYY'); - }, - month: function () { - return publishedAtMoment.format('MM'); - }, - day: function () { - return publishedAtMoment.format('DD'); - }, - author: function () { - return resource.primary_author.slug; - }, - primary_author: function () { - return resource.primary_author ? resource.primary_author.slug : primaryTagFallback; - }, - primary_tag: function () { - return resource.primary_tag ? resource.primary_tag.slug : primaryTagFallback; - }, - slug: function () { - return resource.slug; - }, - id: function () { - return resource.id; - } - }; - - // replace tags like :slug or :year with actual values - output = output.replace(/(:[a-z_]+)/g, function (match) { - if (_.has(permalinkLookUp, match.substr(1))) { - return permalinkLookUp[match.substr(1)](); - } - }); - - return output; -} - -// ## urlFor -// Synchronous url creation for a given context -// Can generate a url for a named path and given path. -// Determines what sort of context it has been given, and delegates to the correct generation method, -// Finally passing to createUrl, to ensure any subdirectory is honoured, and the url is absolute if needed -// Usage: -// urlFor('home', true) -> http://my-ghost-blog.com/ -// E.g. /blog/ subdir -// urlFor({relativeUrl: '/my-static-page/'}) -> /blog/my-static-page/ -// Parameters: -// - context - a string, or json object describing the context for which you need a url -// - data (optional) - a json object containing data needed to generate a url -// - absolute (optional, default:false) - boolean whether or not the url should be absolute -// This is probably not the right place for this, but it's the best place for now -// @TODO: rewrite, very hard to read, create private functions! -function urlFor(context, data, absolute) { - var urlPath = '/', - secure, imagePathRe, - knownObjects = ['image', 'nav'], baseUrl, - hostname, - - // this will become really big - knownPaths = { - home: '/', - sitemap_xsl: '/sitemap.xsl' - }; - - // Make data properly optional - if (_.isBoolean(data)) { - absolute = data; - data = null; - } - - // Can pass 'secure' flag in either context or data arg - secure = (context && context.secure) || (data && data.secure); - - if (_.isObject(context) && context.relativeUrl) { - urlPath = context.relativeUrl; - } else if (_.isString(context) && _.indexOf(knownObjects, context) !== -1) { - if (context === 'image' && data.image) { - urlPath = data.image; - imagePathRe = new RegExp('^' + getSubdir() + '/' + STATIC_IMAGE_URL_PREFIX); - absolute = imagePathRe.test(data.image) ? absolute : false; - - if (absolute) { - // Remove the sub-directory from the URL because ghostConfig will add it back. - urlPath = urlPath.replace(new RegExp('^' + getSubdir()), ''); - baseUrl = getBlogUrl(secure).replace(/\/$/, ''); - urlPath = baseUrl + urlPath; - } - - return urlPath; - } else if (context === 'nav' && data.nav) { - urlPath = data.nav.url; - secure = data.nav.secure || secure; - baseUrl = getBlogUrl(secure); - hostname = baseUrl.split('//')[1]; - - // If the hostname is present in the url - if (urlPath.indexOf(hostname) > -1 - // do no not apply, if there is a subdomain, or a mailto link - && !urlPath.split(hostname)[0].match(/\.|mailto:/) - // do not apply, if there is a port after the hostname - && urlPath.split(hostname)[1].substring(0, 1) !== ':') { - // make link relative to account for possible mismatch in http/https etc, force absolute - urlPath = urlPath.split(hostname)[1]; - urlPath = urlJoin('/', urlPath); - absolute = true; - } - } - } else if (context === 'home' && absolute) { - urlPath = getBlogUrl(secure); - - // CASE: there are cases where urlFor('home') needs to be returned without trailing - // slash e. g. the `{{@site.url}}` helper. See https://github.com/TryGhost/Ghost/issues/8569 - if (data && data.trailingSlash === false) { - urlPath = urlPath.replace(/\/$/, ''); - } - } else if (context === 'admin') { - urlPath = getAdminUrl() || getBlogUrl(); - - if (absolute) { - urlPath += 'ghost/'; - } else { - urlPath = '/ghost/'; - } - } else if (context === 'api') { - urlPath = getAdminUrl() || getBlogUrl(); - let apiPath = getApiPath({version: 'v0.1', type: 'content'}); - // CASE: with or without protocol? If your blog url (or admin url) is configured to http, it's still possible that e.g. nginx allows both https+http. - // So it depends how you serve your blog. The main focus here is to avoid cors problems. - // @TODO: rename cors - if (data && data.cors) { - if (!urlPath.match(/^https:/)) { - urlPath = urlPath.replace(/^.*?:\/\//g, '//'); - } - } - - if (data && data.version) { - apiPath = getApiPath({version: data.version, type: data.versionType}); - } - - if (absolute) { - urlPath = urlPath.replace(/\/$/, '') + apiPath; - } else { - urlPath = apiPath; - } - } else if (_.isString(context) && _.indexOf(_.keys(knownPaths), context) !== -1) { - // trying to create a url for a named path - urlPath = knownPaths[context]; - } - - // This url already has a protocol so is likely an external url to be returned - // or it is an alternative scheme, protocol-less, or an anchor-only path - if (urlPath && (urlPath.indexOf('://') !== -1 || urlPath.match(/^(\/\/|#|[a-zA-Z0-9-]+:)/))) { - return urlPath; - } - - return createUrl(urlPath, absolute, secure); -} - -function isSSL(urlToParse) { - var protocol = url.parse(urlToParse).protocol; - return protocol === 'https:'; -} - -function redirect301(res, redirectUrl) { - res.set({'Cache-Control': 'public, max-age=' + config.get('caching:301:maxAge')}); - return res.redirect(301, redirectUrl); -} - -function redirectToAdmin(status, res, adminPath) { - var redirectUrl = urlJoin(urlFor('admin'), adminPath, '/'); - - if (status === 301) { - return redirect301(res, redirectUrl); - } - return res.redirect(redirectUrl); -} - -/** - * Make absolute URLs - * @param {string} html - * @param {string} siteUrl (blog URL) - * @param {string} itemUrl (URL of current context) - * @returns {object} htmlContent - * @description Takes html, blog url and item url and converts relative url into - * absolute urls. Returns an object. The html string can be accessed by calling `html()` on - * the variable that takes the result of this function - */ -function makeAbsoluteUrls(html, siteUrl, itemUrl, options = {assetsOnly: false}) { - html = html || ''; - const htmlContent = cheerio.load(html, {decodeEntities: false}); - const staticImageUrlPrefixRegex = new RegExp(STATIC_IMAGE_URL_PREFIX); - - // convert relative resource urls to absolute - ['href', 'src'].forEach(function forEach(attributeName) { - htmlContent('[' + attributeName + ']').each(function each(ix, el) { - el = htmlContent(el); - - let attributeValue = el.attr(attributeName); - - // if URL is absolute move on to the next element - try { - const parsed = url.parse(attributeValue); - - if (parsed.protocol) { - return; - } - - // Do not convert protocol relative URLs - if (attributeValue.lastIndexOf('//', 0) === 0) { - return; - } - } catch (e) { - return; - } - - // CASE: don't convert internal links - if (attributeValue[0] === '#') { - return; - } - - if (options.assetsOnly && !attributeValue.match(staticImageUrlPrefixRegex)) { - return; - } - - // compose an absolute URL - // if the relative URL begins with a '/' use the blog URL (including sub-directory) - // as the base URL, otherwise use the post's URL. - const baseUrl = attributeValue[0] === '/' ? siteUrl : itemUrl; - attributeValue = urlJoin(baseUrl, attributeValue); - el.attr(attributeName, attributeValue); - }); - }); - - return htmlContent; -} - -function absoluteToRelative(urlToModify, options) { - options = options || {}; - - const urlObj = url.parse(urlToModify); - const relativePath = urlObj.pathname; - - if (options.withoutSubdirectory) { - const subDir = getSubdir(); - - if (!subDir) { - return relativePath; - } - - const subDirRegex = new RegExp('^' + subDir); - return relativePath.replace(subDirRegex, ''); - } - - return relativePath; -} - -function relativeToAbsolute(url) { - if (!url.startsWith('/') || url.startsWith('//')) { - return url; - } - - return createUrl(url, true); -} - -function deduplicateDoubleSlashes(url) { - return url.replace(/\/\//g, '/'); -} - -module.exports.absoluteToRelative = absoluteToRelative; -module.exports.relativeToAbsolute = relativeToAbsolute; -module.exports.makeAbsoluteUrls = makeAbsoluteUrls; -module.exports.getProtectedSlugs = getProtectedSlugs; -module.exports.getSubdir = getSubdir; -module.exports.urlJoin = urlJoin; -module.exports.urlFor = urlFor; -module.exports.isSSL = isSSL; -module.exports.replacePermalink = replacePermalink; -module.exports.redirectToAdmin = redirectToAdmin; -module.exports.redirect301 = redirect301; -module.exports.createUrl = createUrl; -module.exports.deduplicateDoubleSlashes = deduplicateDoubleSlashes; -module.exports.getApiPath = getApiPath; -module.exports.getVersionPath = getVersionPath; -module.exports.getBlogUrl = getBlogUrl; -module.exports.getSiteUrl = getBlogUrl; - -/** - * If you request **any** image in Ghost, it get's served via - * http://your-blog.com/content/images/2017/01/02/author.png - * - * /content/images/ is a static prefix for serving images! - * - * But internally the image is located for example in your custom content path: - * my-content/another-dir/images/2017/01/02/author.png - */ -module.exports.STATIC_IMAGE_URL_PREFIX = STATIC_IMAGE_URL_PREFIX; diff --git a/core/server/update-check.js b/core/server/update-check.js index 68b3c57eb3..ca51d19acb 100644 --- a/core/server/update-check.js +++ b/core/server/update-check.js @@ -16,7 +16,7 @@ const crypto = require('crypto'), debug = require('ghost-ignition').debug('update-check'), api = require('./api').v2, config = require('./config'), - urlService = require('./services/url'), + urlUtils = require('./lib/url-utils'), common = require('./lib/common'), request = require('./lib/request'), ghostVersion = require('./lib/ghost-version'), @@ -111,7 +111,7 @@ function updateCheckData() { posts = descriptors.posts.value(), users = descriptors.users.value(), npm = descriptors.npm.value(), - blogUrl = url.parse(urlService.utils.urlFor('home', true)), + blogUrl = url.parse(urlUtils.urlFor('home', true)), blogId = blogUrl.hostname + blogUrl.pathname.replace(/\//, '') + hash.value; data.blog_id = crypto.createHash('md5').update(blogId).digest('hex'); diff --git a/core/server/web/admin/app.js b/core/server/web/admin/app.js index 9b9d4dbb7f..6af8258105 100644 --- a/core/server/web/admin/app.js +++ b/core/server/web/admin/app.js @@ -3,7 +3,7 @@ const express = require('express'); const serveStatic = require('express').static; const config = require('../../config'); const constants = require('../../lib/constants'); -const urlService = require('../../services/url'); +const urlUtils = require('../../lib/url-utils'); const shared = require('../shared'); const adminMiddleware = require('./middleware'); @@ -25,7 +25,7 @@ module.exports = function setupAdminApp() { // Ember CLI's live-reload script if (config.get('env') === 'development') { adminApp.get('/ember-cli-live-reload.js', function emberLiveReload(req, res) { - res.redirect(`http://localhost:4200${urlService.utils.getSubdir()}/ghost/ember-cli-live-reload.js`); + res.redirect(`http://localhost:4200${urlUtils.getSubdir()}/ghost/ember-cli-live-reload.js`); }); } diff --git a/core/server/web/admin/middleware.js b/core/server/web/admin/middleware.js index f328188cb9..451dd519b9 100644 --- a/core/server/web/admin/middleware.js +++ b/core/server/web/admin/middleware.js @@ -1,12 +1,12 @@ -const urlService = require('../../services/url'); +const urlUtils = require('../../lib/url-utils'); function redirectAdminUrls(req, res, next) { - const subdir = urlService.utils.getSubdir(), + const subdir = urlUtils.getSubdir(), ghostPathRegex = new RegExp(`^${subdir}/ghost/(.+)`), ghostPathMatch = req.originalUrl.match(ghostPathRegex); if (ghostPathMatch) { - return res.redirect(urlService.utils.urlJoin(urlService.utils.urlFor('admin'), '#', ghostPathMatch[1])); + return res.redirect(urlUtils.urlJoin(urlUtils.urlFor('admin'), '#', ghostPathMatch[1])); } next(); diff --git a/core/server/web/api/index.js b/core/server/web/api/index.js index 2d2d433e5b..bbfa2cded6 100644 --- a/core/server/web/api/index.js +++ b/core/server/web/api/index.js @@ -1,6 +1,6 @@ const debug = require('ghost-ignition').debug('web:api:default:app'); const express = require('express'); -const urlUtils = require('../../services/url/utils'); +const urlUtils = require('../../lib/url-utils'); const errorHandler = require('../shared/middlewares/error-handler'); const membersService = require('../../services/members'); @@ -22,4 +22,4 @@ module.exports = function setupApiApp() { debug('Parent API setup end'); return apiApp; -}; \ No newline at end of file +}; diff --git a/core/server/web/shared/middlewares/admin-redirects.js b/core/server/web/shared/middlewares/admin-redirects.js index 5c762e9e73..7f42fa0cd2 100644 --- a/core/server/web/shared/middlewares/admin-redirects.js +++ b/core/server/web/shared/middlewares/admin-redirects.js @@ -1,9 +1,9 @@ const express = require('express'); -const urlService = require('../../../services/url'); +const urlUtils = require('../../../lib/url-utils'); const adminRedirect = (path) => { return function doRedirect(req, res) { - return urlService.utils.redirectToAdmin(301, res, path); + return urlUtils.redirectToAdmin(301, res, path); }; }; diff --git a/core/server/web/shared/middlewares/api/cors.js b/core/server/web/shared/middlewares/api/cors.js index 02adfa7521..da35c4b486 100644 --- a/core/server/web/shared/middlewares/api/cors.js +++ b/core/server/web/shared/middlewares/api/cors.js @@ -2,7 +2,7 @@ const cors = require('cors'); const url = require('url'); const os = require('os'); const some = require('lodash/some'); -const urlService = require('../../../../services/url'); +const urlUtils = require('../../../../lib/url-utils'); let whitelist = []; const ENABLE_CORS = {origin: true, maxAge: 86400}; @@ -33,8 +33,8 @@ function getIPs() { } function getUrls() { - const blogHost = url.parse(urlService.utils.urlFor('home', true)).hostname; - const adminHost = url.parse(urlService.utils.urlFor('admin', true)).hostname; + const blogHost = url.parse(urlUtils.urlFor('home', true)).hostname; + const adminHost = url.parse(urlUtils.urlFor('admin', true)).hostname; const urls = []; urls.push(blogHost); diff --git a/core/server/web/shared/middlewares/serve-favicon.js b/core/server/web/shared/middlewares/serve-favicon.js index 52a747f062..da21da236a 100644 --- a/core/server/web/shared/middlewares/serve-favicon.js +++ b/core/server/web/shared/middlewares/serve-favicon.js @@ -4,7 +4,7 @@ const crypto = require('crypto'); const config = require('../../../config'); const imageLib = require('../../../lib/image'); const storage = require('../../../adapters/storage'); -const urlService = require('../../../services/url'); +const urlUtils = require('../../../lib/url-utils'); const settingsCache = require('../../../services/settings/cache'); let content; @@ -46,7 +46,7 @@ function serveFavicon() { if (settingsCache.get('icon')) { // depends on the uploaded icon extension if (originalExtension !== requestedExtension) { - return res.redirect(302, urlService.utils.urlFor({relativeUrl: `/favicon${originalExtension}`})); + return res.redirect(302, urlUtils.urlFor({relativeUrl: `/favicon${originalExtension}`})); } storage.getStorage() @@ -64,7 +64,7 @@ function serveFavicon() { // CASE: always redirect to .ico for default icon if (originalExtension !== requestedExtension) { - return res.redirect(302, urlService.utils.urlFor({relativeUrl: '/favicon.ico'})); + return res.redirect(302, urlUtils.urlFor({relativeUrl: '/favicon.ico'})); } fs.readFile(filePath, (err, buf) => { diff --git a/core/server/web/shared/middlewares/serve-public-file.js b/core/server/web/shared/middlewares/serve-public-file.js index f9178dcf5d..ee391f6ba8 100644 --- a/core/server/web/shared/middlewares/serve-public-file.js +++ b/core/server/web/shared/middlewares/serve-public-file.js @@ -2,7 +2,7 @@ const crypto = require('crypto'); const fs = require('fs-extra'); const path = require('path'); const config = require('../../../config'); -const urlService = require('../../../services/url'); +const urlUtils = require('../../../lib/url-utils'); function createPublicFileMiddleware(file, type, maxAge) { let content; @@ -24,8 +24,8 @@ function createPublicFileMiddleware(file, type, maxAge) { let str = buf.toString(); if (type === 'text/xsl' || type === 'text/plain' || type === 'application/javascript') { - str = str.replace(blogRegex, urlService.utils.urlFor('home', true).replace(/\/$/, '')); - str = str.replace(apiRegex, urlService.utils.urlFor('api', {cors: true, version: 'v0.1', versionType: 'content'}, true)); + str = str.replace(blogRegex, urlUtils.urlFor('home', true).replace(/\/$/, '')); + str = str.replace(apiRegex, urlUtils.urlFor('api', {cors: true, version: 'v0.1', versionType: 'content'}, true)); } content = { diff --git a/core/server/web/shared/middlewares/uncapitalise.js b/core/server/web/shared/middlewares/uncapitalise.js index cce2ba061d..919c8ced57 100644 --- a/core/server/web/shared/middlewares/uncapitalise.js +++ b/core/server/web/shared/middlewares/uncapitalise.js @@ -12,7 +12,7 @@ // req.baseUrl = /blog // req.path = /ghost/signin/ -const urlService = require('../../../services/url'); +const urlUtils = require('../../../lib/url-utils'); const common = require('../../../lib/common'); const localUtils = require('../utils'); @@ -48,7 +48,7 @@ const uncapitalise = (req, res, next) => { */ if (/[A-Z]/.test(decodedURI)) { redirectPath = (localUtils.removeOpenRedirectFromUrl((req.originalUrl || req.url).replace(pathToTest, pathToTest.toLowerCase()))); - return urlService.utils.redirect301(res, redirectPath); + return urlUtils.redirect301(res, redirectPath); } next(); diff --git a/core/server/web/shared/middlewares/url-redirects.js b/core/server/web/shared/middlewares/url-redirects.js index de46e2a0c5..090fa69d03 100644 --- a/core/server/web/shared/middlewares/url-redirects.js +++ b/core/server/web/shared/middlewares/url-redirects.js @@ -1,7 +1,7 @@ const url = require('url'); const path = require('path'); const debug = require('ghost-ignition').debug('web:shared:mw:url-redirects'); -const urlService = require('../../../services/url'); +const urlUtils = require('../../../lib/url-utils'); const _private = {}; @@ -28,8 +28,8 @@ _private.redirectUrl = (options) => { }; _private.getAdminRedirectUrl = (options) => { - const blogHostWithProtocol = urlService.utils.urlFor('home', true); - const adminHostWithProtocol = urlService.utils.urlFor('admin', true); + const blogHostWithProtocol = urlUtils.urlFor('home', true); + const adminHostWithProtocol = urlUtils.urlFor('admin', true); const adminHostWithoutProtocol = adminHostWithProtocol.replace(/(^\w+:|^)\/\//, ''); const blogHostWithoutProtocol = blogHostWithProtocol.replace(/(^\w+:|^)\/\//, ''); const requestedHost = options.requestedHost; @@ -37,14 +37,14 @@ _private.getAdminRedirectUrl = (options) => { const queryParameters = options.queryParameters; const secure = options.secure; - debug('getAdminRedirectUrl', requestedHost, requestedUrl, adminHostWithoutProtocol, blogHostWithoutProtocol, urlService.utils.urlJoin(blogHostWithoutProtocol, 'ghost/')); + debug('getAdminRedirectUrl', requestedHost, requestedUrl, adminHostWithoutProtocol, blogHostWithoutProtocol, urlUtils.urlJoin(blogHostWithoutProtocol, 'ghost/')); // CASE: we only redirect the admin access if `admin.url` is configured // If url and admin.url are not equal AND the requested host does not match, redirect. // The first condition is the most important, because it ensures that you have a custom admin url configured, // because we don't force an admin redirect if you have a custom url configured, but no admin url. - if (adminHostWithoutProtocol !== urlService.utils.urlJoin(blogHostWithoutProtocol, 'ghost/') && - adminHostWithoutProtocol !== urlService.utils.urlJoin(requestedHost, urlService.utils.getSubdir(), 'ghost/')) { + if (adminHostWithoutProtocol !== urlUtils.urlJoin(blogHostWithoutProtocol, 'ghost/') && + adminHostWithoutProtocol !== urlUtils.urlJoin(requestedHost, urlUtils.getSubdir(), 'ghost/')) { debug('redirect because admin host does not match'); return _private.redirectUrl({ @@ -55,7 +55,7 @@ _private.getAdminRedirectUrl = (options) => { } // CASE: configured admin url is HTTPS, but request is HTTP - if (urlService.utils.isSSL(adminHostWithProtocol) && !secure) { + if (urlUtils.isSSL(adminHostWithProtocol) && !secure) { debug('redirect because protocol does not match'); return _private.redirectUrl({ @@ -67,7 +67,7 @@ _private.getAdminRedirectUrl = (options) => { }; _private.getBlogRedirectUrl = (options) => { - const blogHostWithProtocol = urlService.utils.urlFor('home', true); + const blogHostWithProtocol = urlUtils.urlFor('home', true); const requestedHost = options.requestedHost; const requestedUrl = options.requestedUrl; const queryParameters = options.queryParameters; @@ -76,7 +76,7 @@ _private.getBlogRedirectUrl = (options) => { debug('getBlogRedirectUrl', requestedHost, requestedUrl, blogHostWithProtocol); // CASE: configured canonical url is HTTPS, but request is HTTP, redirect to requested host + SSL - if (urlService.utils.isSSL(blogHostWithProtocol) && !secure) { + if (urlUtils.isSSL(blogHostWithProtocol) && !secure) { debug('redirect because protocol does not match'); return _private.redirectUrl({ @@ -104,7 +104,7 @@ _private.redirect = (req, res, next, redirectFn) => { if (redirectUrl) { debug(`url redirect to: ${redirectUrl}`); - return urlService.utils.redirect301(res, redirectUrl); + return urlUtils.redirect301(res, redirectUrl); } debug('no url redirect'); diff --git a/core/server/web/site/app.js b/core/server/web/site/app.js index 87207e7de9..fb76b3726d 100644 --- a/core/server/web/site/app.js +++ b/core/server/web/site/app.js @@ -8,13 +8,14 @@ const apps = require('../../services/apps'); const constants = require('../../lib/constants'); const storage = require('../../adapters/storage'); const urlService = require('../../services/url'); +const urlUtils = require('../../lib/url-utils'); const sitemapHandler = require('../../data/xml/sitemap/handler'); const themeMiddleware = require('../../services/themes').middleware; const membersService = require('../../services/members'); const siteRoutes = require('./routes'); const shared = require('../shared'); -const STATIC_IMAGE_URL_PREFIX = `/${urlService.utils.STATIC_IMAGE_URL_PREFIX}`; +const STATIC_IMAGE_URL_PREFIX = `/${urlUtils.STATIC_IMAGE_URL_PREFIX}`; let router; diff --git a/core/test/regression/api/admin_spec.js b/core/test/regression/api/admin_spec.js index 6a73e650b3..73f9efa8d1 100644 --- a/core/test/regression/api/admin_spec.js +++ b/core/test/regression/api/admin_spec.js @@ -7,6 +7,7 @@ var should = require('should'), supertest = require('supertest'), testUtils = require('../../utils/index'), configUtils = require('../../utils/configUtils'), + urlUtils = require('../../utils/urlUtils'), ghost = testUtils.startGhost, common = require('../../../server/lib/common/index'), config = require('../../../server/config/index'), @@ -120,6 +121,7 @@ describe('Admin Routing', function () { before(function () { configUtils.set('url', 'https://localhost:2390'); + urlUtils.stubUrlUtilsFromConfig(); return ghost({forceStart: true}) .then(function (_ghostServer) { @@ -129,6 +131,7 @@ describe('Admin Routing', function () { }); after(function () { + urlUtils.restore(); configUtils.restore(); }); diff --git a/core/test/regression/api/v0.1/public_api_spec.js b/core/test/regression/api/v0.1/public_api_spec.js index 887f9057ef..c599abe1cf 100644 --- a/core/test/regression/api/v0.1/public_api_spec.js +++ b/core/test/regression/api/v0.1/public_api_spec.js @@ -7,6 +7,7 @@ var should = require('should'), testUtils = require('../../../utils/index'), localUtils = require('./utils'), configUtils = require('../../../utils/configUtils'), + urlUtils = require('../../../utils/urlUtils'), config = require('../../../../server/config/index'), models = require('../../../../server/models/index'), ghost = testUtils.startGhost, @@ -42,6 +43,7 @@ describe('Public API', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); it('browse posts', function (done) { @@ -468,6 +470,7 @@ describe('Public API', function () { it('ensure origin header on redirect is not getting lost', function (done) { // NOTE: force a redirect to the admin url configUtils.set('admin:url', 'http://localhost:9999'); + urlUtils.stubUrlUtilsFromConfig(); request.get(localUtils.API.getApiQuery('posts?client_id=ghost-test&client_secret=not_available')) .set('Origin', 'https://example.com') diff --git a/core/test/regression/api/v2/content/posts_spec.js b/core/test/regression/api/v2/content/posts_spec.js index f486784631..594837c0d4 100644 --- a/core/test/regression/api/v2/content/posts_spec.js +++ b/core/test/regression/api/v2/content/posts_spec.js @@ -4,6 +4,7 @@ const _ = require('lodash'); const testUtils = require('../../../../utils'); const localUtils = require('./utils'); const configUtils = require('../../../../utils/configUtils'); +const urlUtils = require('../../../../utils/urlUtils'); const config = require('../../../../../server/config'); const ghost = testUtils.startGhost; @@ -22,6 +23,7 @@ describe('Posts', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); const validKey = localUtils.getValidKey(); @@ -89,6 +91,7 @@ describe('Posts', function () { it('ensure origin header on redirect is not getting lost', function (done) { // NOTE: force a redirect to the admin url configUtils.set('admin:url', 'http://localhost:9999'); + urlUtils.stubUrlUtilsFromConfig(); request.get(localUtils.API.getApiQuery(`posts?key=${validKey}`)) .set('Origin', 'https://example.com') diff --git a/core/test/regression/site/frontend_spec.js b/core/test/regression/site/frontend_spec.js index 9cc32efe26..e8ea49d435 100644 --- a/core/test/regression/site/frontend_spec.js +++ b/core/test/regression/site/frontend_spec.js @@ -10,6 +10,7 @@ var should = require('should'), _ = require('lodash'), testUtils = require('../../utils'), configUtils = require('../../utils/configUtils'), + urlUtils = require('../../utils/urlUtils'), config = require('../../../server/config'), settingsCache = require('../../../server/services/settings/cache'), origCache = _.cloneDeep(settingsCache), @@ -479,6 +480,7 @@ describe('Frontend Routing', function () { before(function () { configUtils.set('url', 'http://localhost/blog'); + urlUtils.stubUrlUtilsFromConfig(); return ghost({forceStart: true, subdir: true}) .then(function (_ghostServer) { @@ -490,6 +492,7 @@ describe('Frontend Routing', function () { after(function () { configUtils.restore(); + urlUtils.restore(); }); it('http://localhost should 404', function (done) { @@ -557,6 +560,7 @@ describe('Frontend Routing', function () { before(function () { configUtils.set('url', 'http://localhost/blog/'); + urlUtils.stubUrlUtilsFromConfig(); return ghost({forceStart: true, subdir: true}) .then(function (_ghostServer) { @@ -567,6 +571,7 @@ describe('Frontend Routing', function () { after(function () { configUtils.restore(); + urlUtils.restore(); }); it('http://localhost should 404', function (done) { @@ -643,6 +648,7 @@ describe('Frontend Routing', function () { before(function () { configUtils.set('url', 'http://localhost:2370/'); + urlUtils.stubUrlUtilsFromConfig(); return ghost({forceStart: true}) .then(function (_ghostServer) { @@ -653,6 +659,7 @@ describe('Frontend Routing', function () { after(function () { configUtils.restore(); + urlUtils.restore(); }); it('should set links to url over non-HTTPS', function (done) { @@ -679,6 +686,7 @@ describe('Frontend Routing', function () { before(function () { configUtils.set('url', 'http://localhost:2370/'); + urlUtils.stubUrlUtilsFromConfig(); return ghost({forceStart: true}) .then(function (_ghostServer) { @@ -689,6 +697,7 @@ describe('Frontend Routing', function () { after(function () { configUtils.restore(); + urlUtils.restore(); }); describe('1 case', function () { diff --git a/core/test/regression/site/site_spec.js b/core/test/regression/site/site_spec.js index de2464a943..5c21b34b53 100644 --- a/core/test/regression/site/site_spec.js +++ b/core/test/regression/site/site_spec.js @@ -4,6 +4,7 @@ const should = require('should'), cheerio = require('cheerio'), testUtils = require('../../utils'), configUtils = require('../../utils/configUtils'), + urlUtils = require('../../utils/urlUtils'), appsService = require('../../../server/services/apps'), settingsService = require('../../../server/services/settings'), themeService = require('../../../server/services/themes'), @@ -37,9 +38,12 @@ describe('Integration - Web - Site', function () { }); }); - beforeEach(function () { + before(function () { configUtils.set('url', 'http://example.com'); + urlUtils.stubUrlUtilsFromConfig(); + }); + beforeEach(function () { sinon.spy(api.posts, 'browse'); }); @@ -49,6 +53,7 @@ describe('Integration - Web - Site', function () { after(function () { configUtils.restore(); + urlUtils.restore(); sinon.restore(); }); @@ -227,8 +232,6 @@ describe('Integration - Web - Site', function () { }); it('serve theme asset', function () { - //configUtils.set('url', 'https://example.com'); - const req = { secure: true, method: 'GET', @@ -309,94 +312,96 @@ describe('Integration - Web - Site', function () { }); }); }); + }); + }); - describe('protocol', function () { - it('blog is https, request is http', function () { - configUtils.set('url', 'https://example.com'); + describe('https', function () { + before(function () { + configUtils.set('url', 'https://example.com'); + urlUtils.stubUrlUtilsFromConfig(); + }); - const req = { - secure: false, - host: 'example.com', - method: 'GET', - url: '/html-ipsum' - }; + after(function () { + urlUtils.restore(); + configUtils.restore(); + }); - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('https://example.com/html-ipsum/'); - }); - }); + describe('protocol', function () { + it('blog is https, request is http', function () { + const req = { + secure: false, + host: 'example.com', + method: 'GET', + url: '/html-ipsum' + }; - it('blog is https, request is http, trailing slash exists already', function () { - configUtils.set('url', 'https://example.com'); - - const req = { - secure: false, - method: 'GET', - url: '/html-ipsum/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('https://example.com/html-ipsum/'); - }); - }); + return testUtils.mocks.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(301); + response.headers.location.should.eql('https://example.com/html-ipsum/'); + }); }); - describe('assets', function () { - it('blog is https, request is http', function () { - configUtils.set('url', 'https://example.com'); + it('blog is https, request is http, trailing slash exists already', function () { + const req = { + secure: false, + method: 'GET', + url: '/html-ipsum/', + host: 'example.com' + }; - const req = { - secure: false, - method: 'GET', - url: '/public/ghost-sdk.js', - host: 'example.com' - }; + return testUtils.mocks.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(301); + response.headers.location.should.eql('https://example.com/html-ipsum/'); + }); + }); + }); - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('https://example.com/public/ghost-sdk.js'); - }); - }); + describe('assets', function () { + it('blog is https, request is http', function () { + const req = { + secure: false, + method: 'GET', + url: '/public/ghost-sdk.js', + host: 'example.com' + }; - it('blog is https, request is http', function () { - configUtils.set('url', 'https://example.com'); + return testUtils.mocks.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(301); + response.headers.location.should.eql('https://example.com/public/ghost-sdk.js'); + }); + }); - const req = { - secure: false, - method: 'GET', - url: '/favicon.png', - host: 'example.com' - }; + it('blog is https, request is http', function () { + const req = { + secure: false, + method: 'GET', + url: '/favicon.png', + host: 'example.com' + }; - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('https://example.com/favicon.png'); - }); - }); + return testUtils.mocks.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(301); + response.headers.location.should.eql('https://example.com/favicon.png'); + }); + }); - it('blog is https, request is http', function () { - configUtils.set('url', 'https://example.com'); + it('blog is https, request is http', function () { + const req = { + secure: false, + method: 'GET', + url: '/assets/css/main.css', + host: 'example.com' + }; - const req = { - secure: false, - method: 'GET', - url: '/assets/css/main.css', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('https://example.com/assets/css/main.css'); - }); - }); + return testUtils.mocks.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(301); + response.headers.location.should.eql('https://example.com/assets/css/main.css'); + }); }); }); }); @@ -446,6 +451,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -564,6 +570,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -624,6 +631,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -725,6 +733,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -810,6 +819,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -948,6 +958,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -1050,6 +1061,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -1119,6 +1131,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -1177,6 +1190,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -1382,6 +1396,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -1666,6 +1681,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -1785,9 +1801,13 @@ describe('Integration - Web - Site', function () { }); }); + before(function () { + configUtils.set('url', 'http://example.com'); + urlUtils.stubUrlUtilsFromConfig(); + }); + beforeEach(function () { const postsAPI = require('../../../server/api/v2/posts-public'); - configUtils.set('url', 'http://example.com'); postSpy = sinon.spy(postsAPI.browse, 'query'); }); @@ -1797,6 +1817,7 @@ describe('Integration - Web - Site', function () { after(function () { configUtils.restore(); + urlUtils.restore(); sinon.restore(); }); @@ -2057,94 +2078,96 @@ describe('Integration - Web - Site', function () { }); }); }); + }); + }); - describe('protocol', function () { - it('blog is https, request is http', function () { - configUtils.set('url', 'https://example.com'); + describe('https', function () { + before(function () { + configUtils.set('url', 'https://example.com'); + urlUtils.stubUrlUtilsFromConfig(); + }); - const req = { - secure: false, - host: 'example.com', - method: 'GET', - url: '/html-ipsum' - }; + after(function () { + urlUtils.restore(); + configUtils.restore(); + }); - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('https://example.com/html-ipsum/'); - }); - }); + describe('protocol', function () { + it('blog is https, request is http', function () { + const req = { + secure: false, + host: 'example.com', + method: 'GET', + url: '/html-ipsum' + }; - it('blog is https, request is http, trailing slash exists already', function () { - configUtils.set('url', 'https://example.com'); - - const req = { - secure: false, - method: 'GET', - url: '/html-ipsum/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('https://example.com/html-ipsum/'); - }); - }); + return testUtils.mocks.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(301); + response.headers.location.should.eql('https://example.com/html-ipsum/'); + }); }); - describe('assets', function () { - it('blog is https, request is http', function () { - configUtils.set('url', 'https://example.com'); + it('blog is https, request is http, trailing slash exists already', function () { + const req = { + secure: false, + method: 'GET', + url: '/html-ipsum/', + host: 'example.com' + }; - const req = { - secure: false, - method: 'GET', - url: '/public/ghost-sdk.js', - host: 'example.com' - }; + return testUtils.mocks.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(301); + response.headers.location.should.eql('https://example.com/html-ipsum/'); + }); + }); + }); - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('https://example.com/public/ghost-sdk.js'); - }); - }); + describe('assets', function () { + it('blog is https, request is http', function () { + const req = { + secure: false, + method: 'GET', + url: '/public/ghost-sdk.js', + host: 'example.com' + }; - it('blog is https, request is http', function () { - configUtils.set('url', 'https://example.com'); + return testUtils.mocks.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(301); + response.headers.location.should.eql('https://example.com/public/ghost-sdk.js'); + }); + }); - const req = { - secure: false, - method: 'GET', - url: '/favicon.png', - host: 'example.com' - }; + it('blog is https, request is http', function () { + const req = { + secure: false, + method: 'GET', + url: '/favicon.png', + host: 'example.com' + }; - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('https://example.com/favicon.png'); - }); - }); + return testUtils.mocks.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(301); + response.headers.location.should.eql('https://example.com/favicon.png'); + }); + }); - it('blog is https, request is http', function () { - configUtils.set('url', 'https://example.com'); + it('blog is https, request is http', function () { + const req = { + secure: false, + method: 'GET', + url: '/assets/css/main.css', + host: 'example.com' + }; - const req = { - secure: false, - method: 'GET', - url: '/assets/css/main.css', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('https://example.com/assets/css/main.css'); - }); - }); + return testUtils.mocks.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(301); + response.headers.location.should.eql('https://example.com/assets/css/main.css'); + }); }); }); }); @@ -2194,6 +2217,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -2312,6 +2336,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -2372,6 +2397,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -2473,6 +2499,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -2558,6 +2585,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -2714,6 +2742,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -2830,6 +2859,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -2899,6 +2929,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -2957,6 +2988,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -3142,6 +3174,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { @@ -3392,6 +3425,7 @@ describe('Integration - Web - Site', function () { afterEach(function () { configUtils.restore(); + urlUtils.restore(); }); after(function () { diff --git a/core/test/unit/adapters/scheduling/post-scheduling/index_spec.js b/core/test/unit/adapters/scheduling/post-scheduling/index_spec.js index a227ba34af..996038c51d 100644 --- a/core/test/unit/adapters/scheduling/post-scheduling/index_spec.js +++ b/core/test/unit/adapters/scheduling/post-scheduling/index_spec.js @@ -9,7 +9,7 @@ var should = require('should'), schedulingUtils = require('../../../../../server/adapters/scheduling/utils'), SchedulingDefault = require('../../../../../server/adapters/scheduling/SchedulingDefault'), postScheduling = require('../../../../../server/adapters/scheduling/post-scheduling'), - urlService = require('../../../../../server/services/url'); + urlUtils = require('../../../../../server/lib/url-utils'); describe('Scheduling: Post Scheduling', function () { var scope = { @@ -68,7 +68,7 @@ describe('Scheduling: Post Scheduling', function () { scope.adapter.schedule.calledWith({ time: moment(scope.post.get('published_at')).valueOf(), - url: urlService.utils.urlJoin(scope.apiUrl, 'schedules', 'posts', scope.post.get('id')) + '?client_id=' + scope.client.get('slug') + '&client_secret=' + scope.client.get('secret'), + url: urlUtils.urlJoin(scope.apiUrl, 'schedules', 'posts', scope.post.get('id')) + '?client_id=' + scope.client.get('slug') + '&client_secret=' + scope.client.get('secret'), extra: { httpMethod: 'PUT', oldTime: null diff --git a/core/test/unit/adapters/storage/utils_spec.js b/core/test/unit/adapters/storage/utils_spec.js index 59d7140238..21a757e7ff 100644 --- a/core/test/unit/adapters/storage/utils_spec.js +++ b/core/test/unit/adapters/storage/utils_spec.js @@ -1,6 +1,6 @@ var should = require('should'), sinon = require('sinon'), - urlService = require('../../../../server/services/url'), + urlUtils = require('../../../../server/lib/url-utils'), // Stuff we are testing storageUtils = require('../../../../server/adapters/storage/utils'); @@ -22,9 +22,9 @@ describe('storage utils', function () { var url = 'http://myblog.com/content/images/2017/07/ghost-logo.png', result; - urlForStub = sinon.stub(urlService.utils, 'urlFor'); + urlForStub = sinon.stub(urlUtils, 'urlFor'); urlForStub.withArgs('home').returns('http://myblog.com/'); - urlGetSubdirStub = sinon.stub(urlService.utils, 'getSubdir'); + urlGetSubdirStub = sinon.stub(urlUtils, 'getSubdir'); urlGetSubdirStub.returns(''); result = storageUtils.getLocalFileStoragePath(url); @@ -39,9 +39,9 @@ describe('storage utils', function () { var url = 'https://myblog.com/content/images/2017/07/ghost-logo.png', result; - urlForStub = sinon.stub(urlService.utils, 'urlFor'); + urlForStub = sinon.stub(urlUtils, 'urlFor'); urlForStub.withArgs('home').returns('http://myblog.com/'); - urlGetSubdirStub = sinon.stub(urlService.utils, 'getSubdir'); + urlGetSubdirStub = sinon.stub(urlUtils, 'getSubdir'); urlGetSubdirStub.returns(''); result = storageUtils.getLocalFileStoragePath(url); @@ -53,9 +53,9 @@ describe('storage utils', function () { var url = 'http://myblog.com/blog/content/images/2017/07/ghost-logo.png', result; - urlForStub = sinon.stub(urlService.utils, 'urlFor'); + urlForStub = sinon.stub(urlUtils, 'urlFor'); urlForStub.withArgs('home').returns('http://myblog.com/'); - urlGetSubdirStub = sinon.stub(urlService.utils, 'getSubdir'); + urlGetSubdirStub = sinon.stub(urlUtils, 'getSubdir'); urlGetSubdirStub.returns('/blog'); result = storageUtils.getLocalFileStoragePath(url); @@ -67,9 +67,9 @@ describe('storage utils', function () { var filePath = '/content/images/2017/07/ghost-logo.png', result; - urlForStub = sinon.stub(urlService.utils, 'urlFor'); + urlForStub = sinon.stub(urlUtils, 'urlFor'); urlForStub.withArgs('home').returns('http://myblog.com/'); - urlGetSubdirStub = sinon.stub(urlService.utils, 'getSubdir'); + urlGetSubdirStub = sinon.stub(urlUtils, 'getSubdir'); urlGetSubdirStub.returns(''); result = storageUtils.getLocalFileStoragePath(filePath); @@ -81,9 +81,9 @@ describe('storage utils', function () { var filePath = '/blog/content/images/2017/07/ghost-logo.png', result; - urlForStub = sinon.stub(urlService.utils, 'urlFor'); + urlForStub = sinon.stub(urlUtils, 'urlFor'); urlForStub.withArgs('home').returns('http://myblog.com/'); - urlGetSubdirStub = sinon.stub(urlService.utils, 'getSubdir'); + urlGetSubdirStub = sinon.stub(urlUtils, 'getSubdir'); urlGetSubdirStub.returns('/blog'); result = storageUtils.getLocalFileStoragePath(filePath); @@ -95,9 +95,9 @@ describe('storage utils', function () { var url = 'http://example-blog.com/ghost-logo.png', result; - urlForStub = sinon.stub(urlService.utils, 'urlFor'); + urlForStub = sinon.stub(urlUtils, 'urlFor'); urlForStub.withArgs('home').returns('http://myblog.com/'); - urlGetSubdirStub = sinon.stub(urlService.utils, 'getSubdir'); + urlGetSubdirStub = sinon.stub(urlUtils, 'getSubdir'); urlGetSubdirStub.returns(''); result = storageUtils.getLocalFileStoragePath(url); @@ -111,9 +111,9 @@ describe('storage utils', function () { var url = 'http://myblog.com/content/images/2017/07/ghost-logo.png', result; - urlForStub = sinon.stub(urlService.utils, 'urlFor'); + urlForStub = sinon.stub(urlUtils, 'urlFor'); urlForStub.withArgs('home').returns('http://myblog.com/'); - urlGetSubdirStub = sinon.stub(urlService.utils, 'getSubdir'); + urlGetSubdirStub = sinon.stub(urlUtils, 'getSubdir'); urlGetSubdirStub.returns(''); result = storageUtils.isLocalImage(url); @@ -128,9 +128,9 @@ describe('storage utils', function () { var url = 'https://myblog.com/content/images/2017/07/ghost-logo.png', result; - urlForStub = sinon.stub(urlService.utils, 'urlFor'); + urlForStub = sinon.stub(urlUtils, 'urlFor'); urlForStub.withArgs('home').returns('http://myblog.com/'); - urlGetSubdirStub = sinon.stub(urlService.utils, 'getSubdir'); + urlGetSubdirStub = sinon.stub(urlUtils, 'getSubdir'); urlGetSubdirStub.returns(''); result = storageUtils.isLocalImage(url); @@ -142,9 +142,9 @@ describe('storage utils', function () { var url = 'http://myblog.com/blog/content/images/2017/07/ghost-logo.png', result; - urlForStub = sinon.stub(urlService.utils, 'urlFor'); + urlForStub = sinon.stub(urlUtils, 'urlFor'); urlForStub.withArgs('home').returns('http://myblog.com/'); - urlGetSubdirStub = sinon.stub(urlService.utils, 'getSubdir'); + urlGetSubdirStub = sinon.stub(urlUtils, 'getSubdir'); urlGetSubdirStub.returns('/blog'); result = storageUtils.isLocalImage(url); @@ -156,9 +156,9 @@ describe('storage utils', function () { var url = '/content/images/2017/07/ghost-logo.png', result; - urlForStub = sinon.stub(urlService.utils, 'urlFor'); + urlForStub = sinon.stub(urlUtils, 'urlFor'); urlForStub.withArgs('home').returns('http://myblog.com/'); - urlGetSubdirStub = sinon.stub(urlService.utils, 'getSubdir'); + urlGetSubdirStub = sinon.stub(urlUtils, 'getSubdir'); urlGetSubdirStub.returns(''); result = storageUtils.isLocalImage(url); @@ -170,9 +170,9 @@ describe('storage utils', function () { var url = '/blog/content/images/2017/07/ghost-logo.png', result; - urlForStub = sinon.stub(urlService.utils, 'urlFor'); + urlForStub = sinon.stub(urlUtils, 'urlFor'); urlForStub.withArgs('home').returns('http://myblog.com/'); - urlGetSubdirStub = sinon.stub(urlService.utils, 'getSubdir'); + urlGetSubdirStub = sinon.stub(urlUtils, 'getSubdir'); urlGetSubdirStub.returns('/blog'); result = storageUtils.isLocalImage(url); @@ -184,9 +184,9 @@ describe('storage utils', function () { var url = 'http://somewebsite.com/ghost-logo.png', result; - urlForStub = sinon.stub(urlService.utils, 'urlFor'); + urlForStub = sinon.stub(urlUtils, 'urlFor'); urlForStub.withArgs('home').returns('http://myblog.com/'); - urlGetSubdirStub = sinon.stub(urlService.utils, 'getSubdir'); + urlGetSubdirStub = sinon.stub(urlUtils, 'getSubdir'); urlGetSubdirStub.returns(''); result = storageUtils.isLocalImage(url); diff --git a/core/test/unit/api/shared/serializers/input/utils/url_spec.js b/core/test/unit/api/shared/serializers/input/utils/url_spec.js index 242573fa1e..1472f2fe26 100644 --- a/core/test/unit/api/shared/serializers/input/utils/url_spec.js +++ b/core/test/unit/api/shared/serializers/input/utils/url_spec.js @@ -1,12 +1,12 @@ const should = require('should'); const sinon = require('sinon'); -const utils = require('../../../../../../../server/services/url/utils'); +const urlUtils = require('../../../../../../../server/lib/url-utils'); const url = require('../../../../../../../server/api/v2/utils/serializers/input/utils/url'); describe('Unit: v2/utils/serializers/input/utils/url', function () { describe('forPost', function () { beforeEach(function () { - sinon.stub(utils, 'getBlogUrl') + sinon.stub(urlUtils, 'getBlogUrl') .returns('https://blogurl.com'); }); diff --git a/core/test/unit/api/v2/utils/serializers/input/posts_spec.js b/core/test/unit/api/v2/utils/serializers/input/posts_spec.js index a6d021ab62..3c6e72d599 100644 --- a/core/test/unit/api/v2/utils/serializers/input/posts_spec.js +++ b/core/test/unit/api/v2/utils/serializers/input/posts_spec.js @@ -1,6 +1,7 @@ const should = require('should'); +const sinon = require('sinon'); const serializers = require('../../../../../../../server/api/v2/utils/serializers'); -const configUtils = require('../../../../../../utils/configUtils'); +const urlUtils = require('../../../../../../utils/urlUtils'); describe('Unit: v2/utils/serializers/input/posts', function () { describe('browse', function () { @@ -221,152 +222,170 @@ describe('Unit: v2/utils/serializers/input/posts', function () { describe('edit', function () { describe('Ensure relative urls are returned for standard image urls', function () { - after(function () { - configUtils.restore(); - }); + describe('no subdir', function () { + let sandbox; - it('when mobiledoc contains an absolute URL to image', function () { - configUtils.set({url: 'https://mysite.com'}); - const apiConfig = {}; - const frame = { - options: { - context: { - user: 0, - api_key: { - id: 1, - type: 'content' + after(function () { + sandbox.restore(); + }); + + before(function () { + sandbox = sinon.createSandbox(); + urlUtils.stubUrlUtils({url: 'https://mysite.com'}, sandbox); + }); + + it('when mobiledoc contains an absolute URL to image', function () { + const apiConfig = {}; + const frame = { + options: { + context: { + user: 0, + api_key: { + id: 1, + type: 'content' + }, }, }, - }, - data: { - posts: [ - { - id: 'id1', - mobiledoc: '{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"https://mysite.com/content/images/2019/02/image.jpg"}]]}' - } - ] - } - }; + data: { + posts: [ + { + id: 'id1', + mobiledoc: '{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"https://mysite.com/content/images/2019/02/image.jpg"}]]}' + } + ] + } + }; - serializers.input.posts.edit(apiConfig, frame); + serializers.input.posts.edit(apiConfig, frame); - let postData = frame.data.posts[0]; - postData.mobiledoc.should.equal('{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"/content/images/2019/02/image.jpg"}]]}'); - }); + let postData = frame.data.posts[0]; + postData.mobiledoc.should.equal('{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"/content/images/2019/02/image.jpg"}]]}'); + }); - it('when mobiledoc contains multiple absolute URLs to images with different protocols', function () { - configUtils.set({url: 'https://mysite.com'}); - const apiConfig = {}; - const frame = { - options: { - context: { - user: 0, - api_key: { - id: 1, - type: 'content' + it('when mobiledoc contains multiple absolute URLs to images with different protocols', function () { + const apiConfig = {}; + const frame = { + options: { + context: { + user: 0, + api_key: { + id: 1, + type: 'content' + }, }, }, - }, - data: { - posts: [ - { - id: 'id1', - mobiledoc: '{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"https://mysite.com/content/images/2019/02/image.jpg"}],["image",{"src":"http://mysite.com/content/images/2019/02/image.png"}]]' - } - ] - } - }; + data: { + posts: [ + { + id: 'id1', + mobiledoc: '{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"https://mysite.com/content/images/2019/02/image.jpg"}],["image",{"src":"http://mysite.com/content/images/2019/02/image.png"}]]' + } + ] + } + }; - serializers.input.posts.edit(apiConfig, frame); + serializers.input.posts.edit(apiConfig, frame); - let postData = frame.data.posts[0]; - postData.mobiledoc.should.equal('{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"/content/images/2019/02/image.jpg"}],["image",{"src":"/content/images/2019/02/image.png"}]]'); + let postData = frame.data.posts[0]; + postData.mobiledoc.should.equal('{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"/content/images/2019/02/image.jpg"}],["image",{"src":"/content/images/2019/02/image.png"}]]'); + }); + + it('when blog url is without subdir', function () { + const apiConfig = {}; + const frame = { + options: { + context: { + user: 0, + api_key: { + id: 1, + type: 'content' + }, + }, + withRelated: ['tags', 'authors'] + }, + data: { + posts: [ + { + id: 'id1', + feature_image: 'https://mysite.com/content/images/image.jpg', + og_image: 'https://mysite.com/mycustomstorage/images/image.jpg', + twitter_image: 'https://mysite.com/blog/content/images/image.jpg', + tags: [{ + id: 'id3', + feature_image: 'http://mysite.com/content/images/image.jpg' + }], + authors: [{ + id: 'id4', + name: 'Ghosty', + profile_image: 'https://somestorage.com/blog/images/image.jpg' + }] + } + ] + } + }; + serializers.input.posts.edit(apiConfig, frame); + let postData = frame.data.posts[0]; + postData.feature_image.should.eql('/content/images/image.jpg'); + postData.og_image.should.eql('https://mysite.com/mycustomstorage/images/image.jpg'); + postData.twitter_image.should.eql('https://mysite.com/blog/content/images/image.jpg'); + postData.tags[0].feature_image.should.eql('/content/images/image.jpg'); + postData.authors[0].profile_image.should.eql('https://somestorage.com/blog/images/image.jpg'); + }); }); - it('when blog url is without subdir', function () { - configUtils.set({url: 'https://mysite.com'}); - const apiConfig = {}; - const frame = { - options: { - context: { - user: 0, - api_key: { - id: 1, - type: 'content' - }, - }, - withRelated: ['tags', 'authors'] - }, - data: { - posts: [ - { - id: 'id1', - feature_image: 'https://mysite.com/content/images/image.jpg', - og_image: 'https://mysite.com/mycustomstorage/images/image.jpg', - twitter_image: 'https://mysite.com/blog/content/images/image.jpg', - tags: [{ - id: 'id3', - feature_image: 'http://mysite.com/content/images/image.jpg' - }], - authors: [{ - id: 'id4', - name: 'Ghosty', - profile_image: 'https://somestorage.com/blog/images/image.jpg' - }] - } - ] - } - }; - serializers.input.posts.edit(apiConfig, frame); - let postData = frame.data.posts[0]; - postData.feature_image.should.eql('/content/images/image.jpg'); - postData.og_image.should.eql('https://mysite.com/mycustomstorage/images/image.jpg'); - postData.twitter_image.should.eql('https://mysite.com/blog/content/images/image.jpg'); - postData.tags[0].feature_image.should.eql('/content/images/image.jpg'); - postData.authors[0].profile_image.should.eql('https://somestorage.com/blog/images/image.jpg'); - }); + describe('with subdir', function () { + let sandbox; - it('when blog url is with subdir', function () { - configUtils.set({url: 'https://mysite.com/blog'}); - const apiConfig = {}; - const frame = { - options: { - context: { - user: 0, - api_key: { - id: 1, - type: 'content' + after(function () { + sandbox.restore(); + }); + + before(function () { + sandbox = sinon.createSandbox(); + urlUtils.stubUrlUtils({url: 'https://mysite.com/blog'}, sandbox); + }); + + it('when blog url is with subdir', function () { + const apiConfig = {}; + const frame = { + options: { + context: { + user: 0, + api_key: { + id: 1, + type: 'content' + }, }, + withRelated: ['tags', 'authors'] }, - withRelated: ['tags', 'authors'] - }, - data: { - posts: [ - { - id: 'id1', - feature_image: 'https://mysite.com/blog/content/images/image.jpg', - og_image: 'https://mysite.com/content/images/image.jpg', - twitter_image: 'https://mysite.com/mycustomstorage/images/image.jpg', - tags: [{ - id: 'id3', - feature_image: 'http://mysite.com/blog/mycustomstorage/content/images/image.jpg' - }], - authors: [{ - id: 'id4', - name: 'Ghosty', - profile_image: 'https://somestorage.com/blog/content/images/image.jpg' - }] - } - ] - } - }; - serializers.input.posts.edit(apiConfig, frame); - let postData = frame.data.posts[0]; - postData.feature_image.should.eql('/blog/content/images/image.jpg'); - postData.og_image.should.eql('https://mysite.com/content/images/image.jpg'); - postData.twitter_image.should.eql('https://mysite.com/mycustomstorage/images/image.jpg'); - postData.tags[0].feature_image.should.eql('http://mysite.com/blog/mycustomstorage/content/images/image.jpg'); - postData.authors[0].profile_image.should.eql('https://somestorage.com/blog/content/images/image.jpg'); + data: { + posts: [ + { + id: 'id1', + feature_image: 'https://mysite.com/blog/content/images/image.jpg', + og_image: 'https://mysite.com/content/images/image.jpg', + twitter_image: 'https://mysite.com/mycustomstorage/images/image.jpg', + tags: [{ + id: 'id3', + feature_image: 'http://mysite.com/blog/mycustomstorage/content/images/image.jpg' + }], + authors: [{ + id: 'id4', + name: 'Ghosty', + profile_image: 'https://somestorage.com/blog/content/images/image.jpg' + }] + } + ] + } + }; + serializers.input.posts.edit(apiConfig, frame); + let postData = frame.data.posts[0]; + postData.feature_image.should.eql('/blog/content/images/image.jpg'); + postData.og_image.should.eql('https://mysite.com/content/images/image.jpg'); + postData.twitter_image.should.eql('https://mysite.com/mycustomstorage/images/image.jpg'); + postData.tags[0].feature_image.should.eql('http://mysite.com/blog/mycustomstorage/content/images/image.jpg'); + postData.authors[0].profile_image.should.eql('https://somestorage.com/blog/content/images/image.jpg'); + }); }); }); diff --git a/core/test/unit/api/v2/utils/serializers/output/utils/url_spec.js b/core/test/unit/api/v2/utils/serializers/output/utils/url_spec.js index cff6fa630f..3e1b996bdd 100644 --- a/core/test/unit/api/v2/utils/serializers/output/utils/url_spec.js +++ b/core/test/unit/api/v2/utils/serializers/output/utils/url_spec.js @@ -2,13 +2,14 @@ const should = require('should'); const sinon = require('sinon'); const testUtils = require('../../../../../../../utils'); const urlService = require('../../../../../../../../server/services/url'); +const urlUtils = require('../../../../../../../../server/lib/url-utils'); const urlUtil = require('../../../../../../../../server/api/v2/utils/serializers/output/utils/url'); describe('Unit: v2/utils/serializers/output/utils/url', () => { beforeEach(() => { sinon.stub(urlService, 'getUrlByResourceId').returns('getUrlByResourceId'); - sinon.stub(urlService.utils, 'urlFor').returns('urlFor'); - sinon.stub(urlService.utils, 'makeAbsoluteUrls').returns({html: sinon.stub()}); + sinon.stub(urlUtils, 'urlFor').returns('urlFor'); + sinon.stub(urlUtils, 'makeAbsoluteUrls').returns({html: sinon.stub()}); }); afterEach(() => { @@ -34,12 +35,12 @@ describe('Unit: v2/utils/serializers/output/utils/url', () => { post.hasOwnProperty('url').should.be.true(); - urlService.utils.urlFor.callCount.should.eql(2); - urlService.utils.urlFor.getCall(0).args.should.eql(['image', {image: 'value'}, true]); - urlService.utils.urlFor.getCall(1).args.should.eql(['home', true]); + urlUtils.urlFor.callCount.should.eql(2); + urlUtils.urlFor.getCall(0).args.should.eql(['image', {image: 'value'}, true]); + urlUtils.urlFor.getCall(1).args.should.eql(['home', true]); - urlService.utils.makeAbsoluteUrls.callCount.should.eql(1); - urlService.utils.makeAbsoluteUrls.getCall(0).args.should.eql([ + urlUtils.makeAbsoluteUrls.callCount.should.eql(1); + urlUtils.makeAbsoluteUrls.getCall(0).args.should.eql([ '## markdown', 'urlFor', 'getUrlByResourceId', diff --git a/core/test/unit/apps/amp/amp_content_spec.js b/core/test/unit/apps/amp/amp_content_spec.js index 685026198c..a47d1ed435 100644 --- a/core/test/unit/apps/amp/amp_content_spec.js +++ b/core/test/unit/apps/amp/amp_content_spec.js @@ -1,6 +1,6 @@ var should = require('should'), rewire = require('rewire'), - configUtils = require('../../../../test/utils/configUtils'), + urlUtils = require('../../../../test/utils/urlUtils'), ampContentHelper = rewire('../../../../server/apps/amp/lib/helpers/amp_content'); // TODO: Amperize really needs to get stubbed, so we can test returning errors @@ -109,12 +109,11 @@ describe('{{amp_content}} helper', function () { describe('Transforms and sanitizes HTML', function () { beforeEach(function () { - configUtils.set({url: 'https://blog.ghost.org/'}); + ampContentHelper.__set__('urlUtils', urlUtils.getInstance({url: 'https://blog.ghost.org/'})); }); afterEach(function () { ampContentHelper.__set__('amperizeCache', {}); - configUtils.restore(); }); it('can transform img tags to amp-img', function (done) { diff --git a/core/test/unit/data/importer/index_spec.js b/core/test/unit/data/importer/index_spec.js index f212d612d9..58d15eec71 100644 --- a/core/test/unit/data/importer/index_spec.js +++ b/core/test/unit/data/importer/index_spec.js @@ -1,5 +1,6 @@ var should = require('should'), sinon = require('sinon'), + rewire = require('rewire'), Promise = require('bluebird'), _ = require('lodash'), testUtils = require('../../../utils'), @@ -10,19 +11,19 @@ var should = require('should'), // Stuff we are testing ImportManager = require('../../../../server/data/importer'), JSONHandler = require('../../../../server/data/importer/handlers/json'), - ImageHandler = require('../../../../server/data/importer/handlers/image'), + ImageHandler = rewire('../../../../server/data/importer/handlers/image'), MarkdownHandler = require('../../../../server/data/importer/handlers/markdown'), DataImporter = require('../../../../server/data/importer/importers/data'), ImageImporter = require('../../../../server/data/importer/importers/image'), storage = require('../../../../server/adapters/storage'), - configUtils = require('../../../utils/configUtils'); + urlUtils = require('../../../utils/urlUtils'); describe('Importer', function () { afterEach(function () { sinon.restore(); - configUtils.restore(); + ImageHandler = rewire('../../../../server/data/importer/handlers/image'); }); describe('ImportManager', function () { @@ -124,9 +125,8 @@ describe('Importer', function () { imageSpy = sinon.stub(ImageHandler, 'loadFile'), mdSpy = sinon.stub(MarkdownHandler, 'loadFile'); + getFileSpy.returns([]); getFileSpy.withArgs(JSONHandler).returns(['/tmp/dir/myFile.json']); - getFileSpy.withArgs(ImageHandler).returns([]); - getFileSpy.withArgs(MarkdownHandler).returns([]); ImportManager.processZip(testZip).then(function (zipResult) { extractSpy.calledOnce.should.be.true(); @@ -418,7 +418,7 @@ describe('Importer', function () { }); it('can load a file (subdirectory)', function (done) { - configUtils.set({url: 'http://localhost:82832/subdir'}); + ImageHandler.__set__('urlUtils', urlUtils.getInstance({url: 'http://localhost:82832/subdir'})); var filename = 'test-image.jpeg', file = [{ diff --git a/core/test/unit/data/meta/amp_url_spec.js b/core/test/unit/data/meta/amp_url_spec.js index fcbb61da8d..8197a3df05 100644 --- a/core/test/unit/data/meta/amp_url_spec.js +++ b/core/test/unit/data/meta/amp_url_spec.js @@ -1,7 +1,7 @@ const should = require('should'), sinon = require('sinon'), rewire = require('rewire'), - urlService = require('../../../../server/services/url'), + urlUtils = require('../../../../server/lib/url-utils'), testUtils = require('../../../utils'); let getAmpUrl = rewire('../../../../server/data/meta/amp_url'); @@ -15,8 +15,8 @@ describe('getAmpUrl', function () { getAmpUrl = rewire('../../../../server/data/meta/amp_url'); getAmpUrl.__set__('getUrl', getUrlStub); - sinon.stub(urlService.utils, 'urlJoin'); - sinon.stub(urlService.utils, 'urlFor').withArgs('home', true).returns('http://localhost:9999'); + sinon.stub(urlUtils, 'urlJoin'); + sinon.stub(urlUtils, 'urlFor').withArgs('home', true).returns('http://localhost:9999'); }); afterEach(function () { @@ -30,12 +30,12 @@ describe('getAmpUrl', function () { post.context = ['post']; getUrlStub.withArgs(post, false).returns('url'); - urlService.utils.urlJoin.withArgs('http://localhost:9999', 'url', 'amp/').returns('url'); + urlUtils.urlJoin.withArgs('http://localhost:9999', 'url', 'amp/').returns('url'); should.exist(getAmpUrl(post)); - urlService.utils.urlJoin.calledOnce.should.be.true(); - urlService.utils.urlFor.calledOnce.should.be.true(); + urlUtils.urlJoin.calledOnce.should.be.true(); + urlUtils.urlFor.calledOnce.should.be.true(); getUrlStub.calledOnce.should.be.true(); }); @@ -47,8 +47,8 @@ describe('getAmpUrl', function () { should.not.exist(getAmpUrl(tag)); - urlService.utils.urlJoin.called.should.be.false(); - urlService.utils.urlFor.called.should.be.false(); + urlUtils.urlJoin.called.should.be.false(); + urlUtils.urlFor.called.should.be.false(); getUrlStub.called.should.be.false(); }); @@ -60,8 +60,8 @@ describe('getAmpUrl', function () { should.not.exist(getAmpUrl(author)); - urlService.utils.urlJoin.called.should.be.false(); - urlService.utils.urlFor.called.should.be.false(); + urlUtils.urlJoin.called.should.be.false(); + urlUtils.urlFor.called.should.be.false(); getUrlStub.called.should.be.false(); }); @@ -73,8 +73,8 @@ describe('getAmpUrl', function () { should.not.exist(getAmpUrl(post)); - urlService.utils.urlJoin.called.should.be.false(); - urlService.utils.urlFor.called.should.be.false(); + urlUtils.urlJoin.called.should.be.false(); + urlUtils.urlFor.called.should.be.false(); getUrlStub.called.should.be.false(); }); }); diff --git a/core/test/unit/data/meta/asset_url_spec.js b/core/test/unit/data/meta/asset_url_spec.js index 61b537022d..eb02ffa4e5 100644 --- a/core/test/unit/data/meta/asset_url_spec.js +++ b/core/test/unit/data/meta/asset_url_spec.js @@ -1,10 +1,14 @@ var should = require('should'), sinon = require('sinon'), - getAssetUrl = require('../../../../server/data/meta/asset_url'), + rewire = require('rewire'), + imageLib = require('../../../../server/lib/image'), settingsCache = require('../../../../server/services/settings/cache'), configUtils = require('../../../utils/configUtils'), + urlUtils = require('../../../utils/urlUtils'), config = configUtils.config; +const getAssetUrl = rewire('../../../../server/data/meta/asset_url'); + describe('getAssetUrl', function () { afterEach(function () { configUtils.restore(); @@ -77,7 +81,7 @@ describe('getAssetUrl', function () { describe('with /blog subdirectory', function () { beforeEach(function () { - configUtils.set({url: 'http://localhost:82832/blog'}); + getAssetUrl.__set__('urlUtils', urlUtils.getInstance({url: 'http://localhost:82832/blog'})); }); it('should return asset url with just context', function () { @@ -102,16 +106,19 @@ describe('getAssetUrl', function () { describe('favicon', function () { it('should not add asset to url if favicon.ico', function () { + sinon.stub(imageLib.blogIcon, 'getIconUrl').returns('/blog/favicon.ico'); var testUrl = getAssetUrl('favicon.ico'); testUrl.should.equal('/blog/favicon.ico'); }); it('should not add asset to url if favicon.png', function () { + sinon.stub(imageLib.blogIcon, 'getIconUrl').returns('/blog/favicon.ico'); var testUrl = getAssetUrl('favicon.png'); testUrl.should.equal('/blog/favicon.ico'); }); it('should return correct favicon path for custom png', function () { + sinon.stub(imageLib.blogIcon, 'getIconUrl').returns('/blog/favicon.png'); sinon.stub(settingsCache, 'get').withArgs('icon').returns('/content/images/2017/04/my-icon.png'); var testUrl = getAssetUrl('favicon.ico'); testUrl.should.equal('/blog/favicon.png'); diff --git a/core/test/unit/data/meta/canonical_url_spec.js b/core/test/unit/data/meta/canonical_url_spec.js index 35b8bf9b97..3ea6fcf45f 100644 --- a/core/test/unit/data/meta/canonical_url_spec.js +++ b/core/test/unit/data/meta/canonical_url_spec.js @@ -1,7 +1,7 @@ const should = require('should'), sinon = require('sinon'), rewire = require('rewire'), - urlService = require('../../../../server/services/url'), + urlUtils = require('../../../../server/lib/url-utils'), testUtils = require('../../../utils'); let getCanonicalUrl = rewire('../../../../server/data/meta/canonical_url'); @@ -15,8 +15,8 @@ describe('getCanonicalUrl', function () { getCanonicalUrl = rewire('../../../../server/data/meta/canonical_url'); getCanonicalUrl.__set__('getUrl', getUrlStub); - sinon.stub(urlService.utils, 'urlJoin'); - sinon.stub(urlService.utils, 'urlFor').withArgs('home', true).returns('http://localhost:9999'); + sinon.stub(urlUtils, 'urlJoin'); + sinon.stub(urlUtils, 'urlFor').withArgs('home', true).returns('http://localhost:9999'); }); afterEach(function () { @@ -27,12 +27,12 @@ describe('getCanonicalUrl', function () { const post = testUtils.DataGenerator.forKnex.createPost(); getUrlStub.withArgs(post, false).returns('/post-url/'); - urlService.utils.urlJoin.withArgs('http://localhost:9999', '/post-url/').returns('canonical url'); + urlUtils.urlJoin.withArgs('http://localhost:9999', '/post-url/').returns('canonical url'); getCanonicalUrl(post).should.eql('canonical url'); - urlService.utils.urlJoin.calledOnce.should.be.true(); - urlService.utils.urlFor.calledOnce.should.be.true(); + urlUtils.urlJoin.calledOnce.should.be.true(); + urlUtils.urlFor.calledOnce.should.be.true(); getUrlStub.calledOnce.should.be.true(); }); @@ -51,23 +51,23 @@ describe('getCanonicalUrl', function () { const post = testUtils.DataGenerator.forKnex.createPost(); getUrlStub.withArgs(post, false).returns('/post-url/amp/'); - urlService.utils.urlJoin.withArgs('http://localhost:9999', '/post-url/amp/').returns('*/amp/'); + urlUtils.urlJoin.withArgs('http://localhost:9999', '/post-url/amp/').returns('*/amp/'); getCanonicalUrl(post).should.eql('*/'); - urlService.utils.urlJoin.calledOnce.should.be.true(); - urlService.utils.urlFor.calledOnce.should.be.true(); + urlUtils.urlJoin.calledOnce.should.be.true(); + urlUtils.urlFor.calledOnce.should.be.true(); getUrlStub.calledOnce.should.be.true(); }); it('should return home if empty secure data', function () { getUrlStub.withArgs({secure: true}, false).returns('/'); - urlService.utils.urlJoin.withArgs('http://localhost:9999', '/').returns('canonical url'); + urlUtils.urlJoin.withArgs('http://localhost:9999', '/').returns('canonical url'); getCanonicalUrl({secure: true}).should.eql('canonical url'); - urlService.utils.urlJoin.calledOnce.should.be.true(); - urlService.utils.urlFor.calledOnce.should.be.true(); + urlUtils.urlJoin.calledOnce.should.be.true(); + urlUtils.urlFor.calledOnce.should.be.true(); getUrlStub.calledOnce.should.be.true(); }); }); diff --git a/core/test/unit/data/meta/paginated_url_spec.js b/core/test/unit/data/meta/paginated_url_spec.js index 101d06cb0b..5d18bcd4c0 100644 --- a/core/test/unit/data/meta/paginated_url_spec.js +++ b/core/test/unit/data/meta/paginated_url_spec.js @@ -1,6 +1,7 @@ var should = require('should'), + sinon = require('sinon'), getPaginatedUrl = require('../../../../server/data/meta/paginated_url'), - configUtils = require('../../../utils/configUtils'); + urlUtils = require('../../../utils/urlUtils'); describe('getPaginatedUrl', function () { var data, getTestUrls; @@ -124,12 +125,15 @@ describe('getPaginatedUrl', function () { }); describe('with /blog subdirectory', function () { + let sandbox; + before(function () { - configUtils.set({url: 'http://localhost:82832/blog'}); + sandbox = sinon.createSandbox(); + urlUtils.stubUrlUtils({url: 'http://localhost:82832/blog'}, sandbox); }); after(function () { - configUtils.restore(); + sandbox.restore(); }); it('should calculate correct urls for index', function () { diff --git a/core/test/unit/data/meta/url_spec.js b/core/test/unit/data/meta/url_spec.js index 9c4a320b0a..f2247b1288 100644 --- a/core/test/unit/data/meta/url_spec.js +++ b/core/test/unit/data/meta/url_spec.js @@ -1,5 +1,6 @@ const should = require('should'), sinon = require('sinon'), + urlUtils = require('../../../../server/lib/url-utils'), urlService = require('../../../../server/services/url'), getUrl = require('../../../../server/data/meta/url'), testUtils = require('../../../utils/'); @@ -7,7 +8,7 @@ const should = require('should'), describe('getUrl', function () { beforeEach(function () { sinon.stub(urlService, 'getUrlByResourceId'); - sinon.stub(urlService.utils, 'urlFor'); + sinon.stub(urlUtils, 'urlFor'); }); afterEach(function () { @@ -27,11 +28,11 @@ describe('getUrl', function () { it('not absolute, not secure', function () { const post = testUtils.DataGenerator.forKnex.createPost({status: 'draft'}); urlService.getUrlByResourceId.withArgs(post.id).returns('/404/'); - urlService.utils.urlFor.withArgs({relativeUrl: '/p/' + post.uuid + '/', secure: undefined}, null, undefined).returns('relative'); + urlUtils.urlFor.withArgs({relativeUrl: '/p/' + post.uuid + '/', secure: undefined}, null, undefined).returns('relative'); let url = getUrl(post); urlService.getUrlByResourceId.calledOnce.should.be.true(); - urlService.utils.urlFor.withArgs({relativeUrl: '/p/' + post.uuid + '/', secure: undefined}, null, undefined) + urlUtils.urlFor.withArgs({relativeUrl: '/p/' + post.uuid + '/', secure: undefined}, null, undefined) .calledOnce.should.be.true(); url.should.eql('relative'); @@ -40,11 +41,11 @@ describe('getUrl', function () { it('absolute, not secure', function () { const post = testUtils.DataGenerator.forKnex.createPost({status: 'draft'}); urlService.getUrlByResourceId.withArgs(post.id).returns('/404/'); - urlService.utils.urlFor.withArgs({relativeUrl: '/p/' + post.uuid + '/', secure: undefined}, null, true).returns('absolute'); + urlUtils.urlFor.withArgs({relativeUrl: '/p/' + post.uuid + '/', secure: undefined}, null, true).returns('absolute'); let url = getUrl(post, true); urlService.getUrlByResourceId.calledOnce.should.be.true(); - urlService.utils.urlFor.withArgs({relativeUrl: '/p/' + post.uuid + '/', secure: undefined}, null, true) + urlUtils.urlFor.withArgs({relativeUrl: '/p/' + post.uuid + '/', secure: undefined}, null, true) .calledOnce.should.be.true(); url.should.eql('absolute'); @@ -54,11 +55,11 @@ describe('getUrl', function () { const post = testUtils.DataGenerator.forKnex.createPost({status: 'draft'}); post.secure = true; urlService.getUrlByResourceId.withArgs(post.id).returns('/404/'); - urlService.utils.urlFor.withArgs({relativeUrl: '/p/' + post.uuid + '/', secure: true}, null, true).returns('absolute secure'); + urlUtils.urlFor.withArgs({relativeUrl: '/p/' + post.uuid + '/', secure: true}, null, true).returns('absolute secure'); let url = getUrl(post, true); urlService.getUrlByResourceId.calledOnce.should.be.true(); - urlService.utils.urlFor.withArgs({relativeUrl: '/p/' + post.uuid + '/', secure: true}, null, true) + urlUtils.urlFor.withArgs({relativeUrl: '/p/' + post.uuid + '/', secure: true}, null, true) .calledOnce.should.be.true(); url.should.eql('absolute secure'); @@ -77,7 +78,7 @@ describe('getUrl', function () { it('should return absolute url for a post and remove /amp/ in url', function () { const data = {relativeUrl: '/*/amp/'}; - urlService.utils.urlFor.withArgs(data, {}, true).returns('absolute/*/amp/'); + urlUtils.urlFor.withArgs(data, {}, true).returns('absolute/*/amp/'); getUrl(data, true).should.eql('absolute/*/'); urlService.getUrlByResourceId.called.should.be.false(); }); @@ -142,7 +143,7 @@ describe('getUrl', function () { current: true }; - urlService.utils.urlFor.withArgs('nav', {nav: data, secure: data.secure}, undefined) + urlUtils.urlFor.withArgs('nav', {nav: data, secure: data.secure}, undefined) .returns('nav url'); getUrl(data).should.equal('nav url'); @@ -156,7 +157,7 @@ describe('getUrl', function () { current: true }; - urlService.utils.urlFor.withArgs('nav', {nav: data, secure: data.secure}, true) + urlUtils.urlFor.withArgs('nav', {nav: data, secure: data.secure}, true) .returns('absolute nav url'); getUrl(data, true).should.equal('absolute nav url'); @@ -165,7 +166,7 @@ describe('getUrl', function () { it('should return `relativeUrl` and remove /amp/ in url', function () { const data = {relativeUrl: '/*/amp/'}; - urlService.utils.urlFor.withArgs(data, {}, undefined).returns(data.relativeUrl); + urlUtils.urlFor.withArgs(data, {}, undefined).returns(data.relativeUrl); getUrl(data).should.eql('/*/'); urlService.getUrlByResourceId.called.should.be.false(); }); diff --git a/core/test/unit/data/xml/sitemap/generator_spec.js b/core/test/unit/data/xml/sitemap/generator_spec.js index 4514862000..8d92defe5d 100644 --- a/core/test/unit/data/xml/sitemap/generator_spec.js +++ b/core/test/unit/data/xml/sitemap/generator_spec.js @@ -3,7 +3,7 @@ const should = require('should'), ObjectId = require('bson-objectid'), _ = require('lodash'), testUtils = require('../../../../utils'), - urlService = require('../../../../../server/services/url'), + urlUtils = require('../../../../../server/lib/url-utils'), IndexGenerator = require('../../../../../server/data/xml/sitemap/index-generator'), PostGenerator = require('../../../../../server/data/xml/sitemap/post-generator'), PageGenerator = require('../../../../../server/data/xml/sitemap/page-generator'), @@ -95,7 +95,7 @@ describe('Generators', function () { describe('fn: getXml', function () { beforeEach(function () { - sinon.stub(urlService.utils, 'urlFor'); + sinon.stub(urlUtils, 'urlFor'); }); it('get cached xml', function () { @@ -109,10 +109,10 @@ describe('Generators', function () { it('compare content output', function () { let idxFirst, idxSecond, idxThird; - urlService.utils.urlFor.withArgs('image', {image: 'post-100.jpg'}, true).returns('http://my-ghost-blog.com/images/post-100.jpg'); - urlService.utils.urlFor.withArgs('image', {image: 'post-200.jpg'}, true).returns('http://my-ghost-blog.com/images/post-200.jpg'); - urlService.utils.urlFor.withArgs('image', {image: 'post-300.jpg'}, true).returns('http://my-ghost-blog.com/images/post-300.jpg'); - urlService.utils.urlFor.withArgs('sitemap_xsl', true).returns('http://my-ghost-blog.com/sitemap.xsl'); + urlUtils.urlFor.withArgs('image', {image: 'post-100.jpg'}, true).returns('http://my-ghost-blog.com/images/post-100.jpg'); + urlUtils.urlFor.withArgs('image', {image: 'post-200.jpg'}, true).returns('http://my-ghost-blog.com/images/post-200.jpg'); + urlUtils.urlFor.withArgs('image', {image: 'post-300.jpg'}, true).returns('http://my-ghost-blog.com/images/post-300.jpg'); + urlUtils.urlFor.withArgs('sitemap_xsl', true).returns('http://my-ghost-blog.com/sitemap.xsl'); generator.addUrl('http://my-ghost-blog.com/url/100/', testUtils.DataGenerator.forKnex.createPost({ feature_image: 'post-100.jpg', diff --git a/core/test/unit/helpers/asset_spec.js b/core/test/unit/helpers/asset_spec.js index 255dbfe045..8403327419 100644 --- a/core/test/unit/helpers/asset_spec.js +++ b/core/test/unit/helpers/asset_spec.js @@ -1,3 +1,6 @@ +// NOTE: the sole purpose of this suite is to test is it calls through to getAssetUrlHelper +// more complicated use cases are tested directly in asset_url.spec + var should = require('should'), sinon = require('sinon'), configUtils = require('../../utils/configUtils'), @@ -70,58 +73,4 @@ describe('{{asset}} helper', function () { String(rendered).should.equal('/assets/js/asset.min.js?v=abc'); }); }); - - describe('with /blog subdirectory', function () { - before(function () { - configUtils.set({url: 'http://localhost:82832/blog'}); - }); - - it('handles favicon correctly', function () { - rendered = helpers.asset('favicon.ico'); - should.exist(rendered); - String(rendered).should.equal('/blog/favicon.ico'); - }); - - it('handles ghost.css for default templates correctly', function () { - rendered = helpers.asset('public/ghost.css'); - should.exist(rendered); - String(rendered).should.equal('/blog/public/ghost.css?v=abc'); - }); - - it('handles custom favicon correctly', function () { - localSettingsCache.icon = '/content/images/favicon.png'; - - // with png - rendered = helpers.asset('favicon.png'); - should.exist(rendered); - String(rendered).should.equal('/blog/favicon.png'); - - localSettingsCache.icon = '/content/images/favicon.ico'; - - // with ico - rendered = helpers.asset('favicon.ico'); - should.exist(rendered); - String(rendered).should.equal('/blog/favicon.ico'); - }); - - it('handles public assets correctly', function () { - rendered = helpers.asset('public/asset.js'); - should.exist(rendered); - String(rendered).should.equal('/blog/public/asset.js?v=abc'); - }); - - it('handles theme assets correctly', function () { - rendered = helpers.asset('js/asset.js'); - should.exist(rendered); - String(rendered).should.equal('/blog/assets/js/asset.js?v=abc'); - }); - - it('handles hasMinFile assets correctly', function () { - rendered = helpers.asset('js/asset.js', {hash: {hasMinFile: true}}); - should.exist(rendered); - String(rendered).should.equal('/blog/assets/js/asset.min.js?v=abc'); - }); - - configUtils.restore(); - }); }); diff --git a/core/test/unit/helpers/ghost_head_spec.js b/core/test/unit/helpers/ghost_head_spec.js index a90f5a2047..4a45739e84 100644 --- a/core/test/unit/helpers/ghost_head_spec.js +++ b/core/test/unit/helpers/ghost_head_spec.js @@ -3,6 +3,7 @@ const should = require('should'), _ = require('lodash'), moment = require('moment'), testUtils = require('../../utils'), + testUrlUtils = require('../../utils/urlUtils'), configUtils = require('../../utils/configUtils'), themes = require('../../../server/services/themes'), models = require('../../../server/models'), @@ -296,8 +297,16 @@ describe('{{ghost_head}} helper', function () { }); describe('without Code Injection', function () { + let sandbox; + before(function () { - configUtils.set('url', 'http://localhost:65530/'); + sandbox = sinon.createSandbox(); + + testUrlUtils.stubUrlUtils({url: 'http://localhost:65530/'}, sandbox); + }); + + after(function () { + sandbox.restore(); }); it('returns meta tag string on paginated index page without structured data and schema', function (done) { @@ -1234,50 +1243,62 @@ describe('{{ghost_head}} helper', function () { done(); }).catch(done); }); + }); - describe('with /blog subdirectory', function () { - before(function () { - settingsCache.get.withArgs('icon').returns('/content/images/favicon.png'); + describe('with /blog subdirectory', function () { + let sandbox; - configUtils.set({ - url: 'http://localhost:65530/blog/' - }); + before(function () { + sandbox = sinon.createSandbox(); - routing.registry.getRssUrl.returns('http://localhost:65530/blog/rss/'); - }); + settingsCache.get.withArgs('icon').returns('/content/images/favicon.png'); - after(function () { - routing.registry.getRssUrl.returns('http://localhost:65530/rss/'); - }); + testUrlUtils.stubUrlUtils({url: 'http://localhost:65530/blog'}, sandbox); - it('returns correct rss url with subdirectory', function (done) { - helpers.ghost_head(testUtils.createHbsResponse({ - locals: { - context: ['paged', 'index'], - safeVersion: '0.3' - } - })).then(function (rendered) { - should.exist(rendered); - rendered.string.should.match(//); - rendered.string.should.match(//); - rendered.string.should.match(//); - rendered.string.should.match(//); - rendered.string.should.not.match(//); + rendered.string.should.match(//); + rendered.string.should.match(//); + rendered.string.should.match(//); + rendered.string.should.not.match(/body {background: red;}'); - configUtils.set({ - url: 'http://localhost:65530/' - }); + testUrlUtils.stubUrlUtils({url: 'http://localhost:65530/'}, sandbox); + }); + + after(function () { + sandbox.restore(); }); it('returns meta tag plus injected code', function (done) { @@ -1456,10 +1490,15 @@ describe('{{ghost_head}} helper', function () { }); describe('with Ajax Helper', function () { + let sandbox; + before(function () { - configUtils.set({ - url: 'http://localhost:65530/' - }); + sandbox = sinon.createSandbox(); + testUrlUtils.stubUrlUtils({url: 'http://localhost:65530/'}, sandbox); + }); + + after(function () { + sandbox.restore(); }); it('renders script tag with src', function (done) { @@ -1530,10 +1569,6 @@ describe('{{ghost_head}} helper', function () { describe('amp is disabled', function () { before(function () { - configUtils.set({ - url: 'http://localhost:65530/' - }); - settingsCache.get.withArgs('amp').returns(false); }); diff --git a/core/test/unit/helpers/img_url_spec.js b/core/test/unit/helpers/img_url_spec.js index aa7cfec88a..acff348aa1 100644 --- a/core/test/unit/helpers/img_url_spec.js +++ b/core/test/unit/helpers/img_url_spec.js @@ -1,6 +1,6 @@ var should = require('should'), sinon = require('sinon'), - configUtils = require('../../utils/configUtils'), + urlUtils = require('../../utils/urlUtils'), // Stuff we are testing helpers = require('../../../server/helpers'), @@ -9,10 +9,6 @@ var should = require('should'), describe('{{image}} helper', function () { var logWarnStub; - before(function () { - configUtils.set({url: 'http://localhost:82832/'}); - }); - beforeEach(function () { logWarnStub = sinon.stub(common.logging, 'warn'); }); @@ -21,68 +17,81 @@ describe('{{image}} helper', function () { sinon.restore(); }); - after(function () { - configUtils.restore(); - }); + describe('without sub-directory', function () { + let sandbox; - it('should output relative url of image', function () { - var rendered = helpers.img_url('/content/images/image-relative-url.png', {}); - should.exist(rendered); - rendered.should.equal('/content/images/image-relative-url.png'); - logWarnStub.called.should.be.false(); - }); + before(function () { + sandbox = sinon.createSandbox(); + urlUtils.stubUrlUtils({url: 'http://localhost:82832/'}, sandbox); + }); - it('should output relative url of image if the input is absolute', function () { - var rendered = helpers.img_url('http://localhost:82832/content/images/image-relative-url.png', {}); - should.exist(rendered); - rendered.should.equal('/content/images/image-relative-url.png'); - logWarnStub.called.should.be.false(); - }); + after(function () { + sandbox.restore(); + }); - it('should output absolute url of image if the option is present ', function () { - var rendered = helpers.img_url('/content/images/image-relative-url.png', {hash: {absolute: 'true'}}); - should.exist(rendered); - rendered.should.equal('http://localhost:82832/content/images/image-relative-url.png'); - logWarnStub.called.should.be.false(); - }); + it('should output relative url of image', function () { + var rendered = helpers.img_url('/content/images/image-relative-url.png', {}); + should.exist(rendered); + rendered.should.equal('/content/images/image-relative-url.png'); + logWarnStub.called.should.be.false(); + }); - it('should NOT output absolute url of image if the option is "false" ', function () { - var rendered = helpers.img_url('/content/images/image-relative-url.png', {hash: {absolute: 'false'}}); - should.exist(rendered); - rendered.should.equal('/content/images/image-relative-url.png'); - }); + it('should output relative url of image if the input is absolute', function () { + var rendered = helpers.img_url('http://localhost:82832/content/images/image-relative-url.png', {}); + should.exist(rendered); + rendered.should.equal('/content/images/image-relative-url.png'); + logWarnStub.called.should.be.false(); + }); - it('should output author url', function () { - var rendered = helpers.img_url('/content/images/author-image-relative-url.png', {}); - should.exist(rendered); - rendered.should.equal('/content/images/author-image-relative-url.png'); - logWarnStub.called.should.be.false(); - }); + it('should output absolute url of image if the option is present ', function () { + var rendered = helpers.img_url('/content/images/image-relative-url.png', {hash: {absolute: 'true'}}); + should.exist(rendered); + rendered.should.equal('http://localhost:82832/content/images/image-relative-url.png'); + logWarnStub.called.should.be.false(); + }); - it('should have no output if the image attributeis not provided (with warning)', function () { - var rendered = helpers.img_url({hash: {absolute: 'true'}}); - should.not.exist(rendered); - logWarnStub.calledOnce.should.be.true(); - }); + it('should NOT output absolute url of image if the option is "false" ', function () { + var rendered = helpers.img_url('/content/images/image-relative-url.png', {hash: {absolute: 'false'}}); + should.exist(rendered); + rendered.should.equal('/content/images/image-relative-url.png'); + }); - it('should have no output if the image attribute evaluates to undefined (with warning)', function () { - var rendered = helpers.img_url(undefined, {hash: {absolute: 'true'}}); - should.not.exist(rendered); - logWarnStub.calledOnce.should.be.true(); - }); + it('should output author url', function () { + var rendered = helpers.img_url('/content/images/author-image-relative-url.png', {}); + should.exist(rendered); + rendered.should.equal('/content/images/author-image-relative-url.png'); + logWarnStub.called.should.be.false(); + }); - it('should have no output if the image attribute evaluates to null (no waring)', function () { - var rendered = helpers.img_url(null, {hash: {absolute: 'true'}}); - should.not.exist(rendered); - logWarnStub.calledOnce.should.be.false(); + it('should have no output if the image attributeis not provided (with warning)', function () { + var rendered = helpers.img_url({hash: {absolute: 'true'}}); + should.not.exist(rendered); + logWarnStub.calledOnce.should.be.true(); + }); + + it('should have no output if the image attribute evaluates to undefined (with warning)', function () { + var rendered = helpers.img_url(undefined, {hash: {absolute: 'true'}}); + should.not.exist(rendered); + logWarnStub.calledOnce.should.be.true(); + }); + + it('should have no output if the image attribute evaluates to null (no waring)', function () { + var rendered = helpers.img_url(null, {hash: {absolute: 'true'}}); + should.not.exist(rendered); + logWarnStub.calledOnce.should.be.false(); + }); }); describe('with sub-directory', function () { + let sandbox; + before(function () { - configUtils.set({url: 'http://localhost:82832/blog'}); + sandbox = sinon.createSandbox(); + urlUtils.stubUrlUtils({url: 'http://localhost:82832/blog'}, sandbox); }); + after(function () { - configUtils.restore(); + sandbox.restore(); }); it('should output relative url of image', function () { @@ -105,12 +114,17 @@ describe('{{image}} helper', function () { }); describe('image_sizes', function () { + let sandbox; + before(function () { - configUtils.set({url: 'http://localhost:82832'}); + sandbox = sinon.createSandbox(); + urlUtils.stubUrlUtils({url: 'http://localhost:82832/'}, sandbox); }); + after(function () { - configUtils.restore(); + sandbox.restore(); }); + it('should output correct url for absolute paths which are internal', function () { var rendered = helpers.img_url('http://localhost:82832/content/images/my-coole-img.jpg', { hash: { diff --git a/core/test/unit/helpers/url_spec.js b/core/test/unit/helpers/url_spec.js index 56ab6d15fa..070442902f 100644 --- a/core/test/unit/helpers/url_spec.js +++ b/core/test/unit/helpers/url_spec.js @@ -2,18 +2,15 @@ var should = require('should'), sinon = require('sinon'), Promise = require('bluebird'), testUtils = require('../../utils'), - configUtils = require('../../utils/configUtils'), + urlUtils = require('../../utils/urlUtils'), markdownToMobiledoc = require('../../utils/fixtures/data-generator').markdownToMobiledoc, helpers = require('../../../server/helpers'), urlService = require('../../../server/services/url'), api = require('../../../server/api'); describe('{{url}} helper', function () { - var rendered; - - before(function () { - configUtils.set({url: 'http://localhost:82832/'}); - }); + let rendered; + let sandbox; beforeEach(function () { rendered = null; @@ -29,246 +26,263 @@ describe('{{url}} helper', function () { sinon.restore(); }); - after(function () { - configUtils.restore(); - }); - - it('should return the slug with a prefix slash if the context is a post', function () { - const post = testUtils.DataGenerator.forKnex.createPost({ - html: 'content', - mobiledoc: markdownToMobiledoc('ff'), - title: 'title', - slug: 'slug', - created_at: new Date(0), - url: '/slug/' + describe('no subdir', function () { + before(function () { + sandbox = sinon.createSandbox(); + urlUtils.stubUrlUtils({url: 'http://localhost:82832/'}, sandbox); }); - urlService.getUrlByResourceId.withArgs(post.id, {absolute: undefined, secure: undefined, withSubdirectory: true}).returns('/slug/'); - - rendered = helpers.url.call(post); - should.exist(rendered); - rendered.string.should.equal('/slug/'); - }); - - it('should output an absolute URL if the option is present', function () { - const post = testUtils.DataGenerator.forKnex.createPost({ - html: 'content', - mobiledoc: markdownToMobiledoc('ff'), - title: 'title', - slug: 'slug', - url: '/slug/', - created_at: new Date(0) + after(function () { + sandbox.restore(); }); - urlService.getUrlByResourceId.withArgs(post.id, {absolute: true, secure: undefined, withSubdirectory: true}).returns('http://localhost:82832/slug/'); + it('should return the slug with a prefix slash if the context is a post', function () { + const post = testUtils.DataGenerator.forKnex.createPost({ + html: 'content', + mobiledoc: markdownToMobiledoc('ff'), + title: 'title', + slug: 'slug', + created_at: new Date(0), + url: '/slug/' + }); - rendered = helpers.url.call(post, {hash: {absolute: 'true'}}); - should.exist(rendered); - rendered.string.should.equal('http://localhost:82832/slug/'); - }); + urlService.getUrlByResourceId.withArgs(post.id, {absolute: undefined, secure: undefined, withSubdirectory: true}).returns('/slug/'); - it('should output an absolute URL with https if the option is present and secure', function () { - const post = testUtils.DataGenerator.forKnex.createPost({ - html: 'content', - mobiledoc: markdownToMobiledoc('ff'), - title: 'title', - slug: 'slug', - url: '/slug/', - created_at: new Date(0), - secure: true + rendered = helpers.url.call(post); + should.exist(rendered); + rendered.string.should.equal('/slug/'); }); - urlService.getUrlByResourceId.withArgs(post.id, {absolute: true, secure: true, withSubdirectory: true}).returns('https://localhost:82832/slug/'); + it('should output an absolute URL if the option is present', function () { + const post = testUtils.DataGenerator.forKnex.createPost({ + html: 'content', + mobiledoc: markdownToMobiledoc('ff'), + title: 'title', + slug: 'slug', + url: '/slug/', + created_at: new Date(0) + }); - rendered = helpers.url.call(post, {hash: {absolute: 'true'}}); - should.exist(rendered); - rendered.string.should.equal('https://localhost:82832/slug/'); - }); + urlService.getUrlByResourceId.withArgs(post.id, {absolute: true, secure: undefined, withSubdirectory: true}).returns('http://localhost:82832/slug/'); - it('should return the slug with a prefixed /tag/ if the context is a tag', function () { - const tag = testUtils.DataGenerator.forKnex.createTag({ - name: 'the tag', - slug: 'the-tag', - description: null, - parent: null + rendered = helpers.url.call(post, {hash: {absolute: 'true'}}); + should.exist(rendered); + rendered.string.should.equal('http://localhost:82832/slug/'); }); - urlService.getUrlByResourceId.withArgs(tag.id, {absolute: undefined, secure: undefined, withSubdirectory: true}).returns('/tag/the-tag/'); - - rendered = helpers.url.call(tag); - should.exist(rendered); - rendered.string.should.equal('/tag/the-tag/'); - }); - - it('should return the slug with a prefixed /author/ if the context is author', function () { - const user = testUtils.DataGenerator.forKnex.createUser({ - bio: null, - website: null, - profile_image: null, - location: null, - slug: 'some-author' - }); - - urlService.getUrlByResourceId.withArgs(user.id, {absolute: undefined, secure: undefined, withSubdirectory: true}).returns('/author/some-author/'); - - rendered = helpers.url.call(user); - should.exist(rendered); - rendered.string.should.equal('/author/some-author/'); - }); - - it('should return / if not a post or tag', function () { - rendered = helpers.url.call({something: 'key'}); - should.exist(rendered); - rendered.string.should.equal('/'); - }); - - it('should return a relative url if passed through a nav context', function () { - rendered = helpers.url.call( - {url: '/foo', label: 'Foo', slug: 'foo', current: true}); - should.exist(rendered); - rendered.string.should.equal('/foo'); - }); - - it('should return an absolute url if passed through a nav context', function () { - rendered = helpers.url.call( - {url: '/bar', label: 'Bar', slug: 'bar', current: true}, - {hash: {absolute: 'true'}}); - should.exist(rendered); - rendered.string.should.equal('http://localhost:82832/bar'); - }); - - it('should return an absolute url with https if context is secure', function () { - rendered = helpers.url.call( - {url: '/bar', label: 'Bar', slug: 'bar', current: true, secure: true}, - {hash: {absolute: 'true'}}); - should.exist(rendered); - rendered.string.should.equal('https://localhost:82832/bar'); - }); - - it('external urls should be retained in a nav context', function () { - rendered = helpers.url.call( - {url: 'http://casper.website/baz', label: 'Baz', slug: 'baz', current: true}, - {hash: {absolute: 'true'}}); - should.exist(rendered); - rendered.string.should.equal('http://casper.website/baz'); - }); - - it('should handle hosted urls in a nav context', function () { - rendered = helpers.url.call( - {url: 'http://localhost:82832/qux', label: 'Qux', slug: 'qux', current: true}, - {hash: {absolute: 'true'}}); - should.exist(rendered); - rendered.string.should.equal('http://localhost:82832/qux'); - }); - - it('should handle hosted urls in a nav context with secure', function () { - rendered = helpers.url.call( - { - url: 'http://localhost:82832/qux', label: 'Qux', slug: 'qux', current: true, + it('should output an absolute URL with https if the option is present and secure', function () { + const post = testUtils.DataGenerator.forKnex.createPost({ + html: 'content', + mobiledoc: markdownToMobiledoc('ff'), + title: 'title', + slug: 'slug', + url: '/slug/', + created_at: new Date(0), secure: true - }, - {hash: {absolute: 'true'}}); - should.exist(rendered); - rendered.string.should.equal('https://localhost:82832/qux'); - }); + }); - it('should handle hosted https urls in a nav context with secure', function () { - rendered = helpers.url.call( - { - url: 'https://localhost:82832/qux', label: 'Qux', slug: 'qux', current: true, - secure: true - }, - {hash: {absolute: 'true'}}); - should.exist(rendered); - rendered.string.should.equal('https://localhost:82832/qux'); - }); + urlService.getUrlByResourceId.withArgs(post.id, {absolute: true, secure: true, withSubdirectory: true}).returns('https://localhost:82832/slug/'); - it('should handle hosted urls with the wrong protocol in a nav context', function () { - rendered = helpers.url.call( - {url: 'https://localhost:82832/quux', label: 'Quux', slug: 'quux', current: true}, - {hash: {absolute: 'true'}}); - should.exist(rendered); - rendered.string.should.equal('http://localhost:82832/quux'); - }); + rendered = helpers.url.call(post, {hash: {absolute: 'true'}}); + should.exist(rendered); + rendered.string.should.equal('https://localhost:82832/slug/'); + }); - it('should pass through protocol-less URLs regardless of absolute setting', function () { - rendered = helpers.url.call( - {url: '//casper.website/baz', label: 'Baz', slug: 'baz', current: true}, - {hash: {}}); - should.exist(rendered); - rendered.string.should.equal('//casper.website/baz'); + it('should return the slug with a prefixed /tag/ if the context is a tag', function () { + const tag = testUtils.DataGenerator.forKnex.createTag({ + name: 'the tag', + slug: 'the-tag', + description: null, + parent: null + }); - rendered = helpers.url.call( - {url: '//casper.website/baz', label: 'Baz', slug: 'baz', current: true}, - {hash: {absolute: 'true'}}); - should.exist(rendered); - rendered.string.should.equal('//casper.website/baz'); - }); + urlService.getUrlByResourceId.withArgs(tag.id, {absolute: undefined, secure: undefined, withSubdirectory: true}).returns('/tag/the-tag/'); - it('should pass through URLs with alternative schemes regardless of absolute setting', function () { - rendered = helpers.url.call( - {url: 'tel:01234567890', label: 'Baz', slug: 'baz', current: true}, - {hash: {}}); - should.exist(rendered); - rendered.string.should.equal('tel:01234567890'); + rendered = helpers.url.call(tag); + should.exist(rendered); + rendered.string.should.equal('/tag/the-tag/'); + }); - rendered = helpers.url.call( - {url: 'mailto:example@ghost.org', label: 'Baz', slug: 'baz', current: true}, - {hash: {}}); - should.exist(rendered); - rendered.string.should.equal('mailto:example@ghost.org'); + it('should return the slug with a prefixed /author/ if the context is author', function () { + const user = testUtils.DataGenerator.forKnex.createUser({ + bio: null, + website: null, + profile_image: null, + location: null, + slug: 'some-author' + }); - rendered = helpers.url.call( - {url: 'tel:01234567890', label: 'Baz', slug: 'baz', current: true}, - {hash: {absolute: 'true'}}); - should.exist(rendered); - rendered.string.should.equal('tel:01234567890'); + urlService.getUrlByResourceId.withArgs(user.id, {absolute: undefined, secure: undefined, withSubdirectory: true}).returns('/author/some-author/'); - rendered = helpers.url.call( - {url: 'mailto:example@ghost.org', label: 'Baz', slug: 'baz', current: true}, - {hash: {absolute: 'true'}}); - should.exist(rendered); - rendered.string.should.equal('mailto:example@ghost.org'); - }); + rendered = helpers.url.call(user); + should.exist(rendered); + rendered.string.should.equal('/author/some-author/'); + }); - it('should pass through anchor-only URLs regardless of absolute setting', function () { - rendered = helpers.url.call( - {url: '#thatsthegoodstuff', label: 'Baz', slug: 'baz', current: true}, - {hash: {}}); - should.exist(rendered); - rendered.string.should.equal('#thatsthegoodstuff'); + it('should return / if not a post or tag', function () { + rendered = helpers.url.call({something: 'key'}); + should.exist(rendered); + rendered.string.should.equal('/'); + }); - rendered = helpers.url.call( - {url: '#thatsthegoodstuff', label: 'Baz', slug: 'baz', current: true}, - {hash: {absolute: 'true'}}); - should.exist(rendered); - rendered.string.should.equal('#thatsthegoodstuff'); - }); + it('should return a relative url if passed through a nav context', function () { + rendered = helpers.url.call( + {url: '/foo', label: 'Foo', slug: 'foo', current: true}); + should.exist(rendered); + rendered.string.should.equal('/foo'); + }); - it('should not HTML-escape URLs', function () { - rendered = helpers.url.call( - {url: '/foo?foo=bar&baz=qux', label: 'Foo', slug: 'foo', current: true}); - should.exist(rendered); - rendered.string.should.equal('/foo?foo=bar&baz=qux'); - }); + it('should return an absolute url if passed through a nav context', function () { + rendered = helpers.url.call( + {url: '/bar', label: 'Bar', slug: 'bar', current: true}, + {hash: {absolute: 'true'}}); + should.exist(rendered); + rendered.string.should.equal('http://localhost:82832/bar'); + }); - it('should encode URLs', function () { - rendered = helpers.url.call( - {url: '/foo?foo=bar&baz=qux&', label: 'Foo', slug: 'foo', current: true}); - should.exist(rendered); - rendered.string.should.equal('/foo?foo=bar&baz=qux&%3Cscript%3Ealert(%22gotcha%22)%3C/script%3E'); - }); + it('should return an absolute url with https if context is secure', function () { + rendered = helpers.url.call( + {url: '/bar', label: 'Bar', slug: 'bar', current: true, secure: true}, + {hash: {absolute: 'true'}}); + should.exist(rendered); + rendered.string.should.equal('https://localhost:82832/bar'); + }); - it('should not double-encode URLs', function () { - rendered = helpers.url.call( - {url: '/?foo=space%20bar', label: 'Foo', slug: 'foo', current: true}); - should.exist(rendered); - rendered.string.should.equal('/?foo=space%20bar'); + it('external urls should be retained in a nav context', function () { + rendered = helpers.url.call( + {url: 'http://casper.website/baz', label: 'Baz', slug: 'baz', current: true}, + {hash: {absolute: 'true'}}); + should.exist(rendered); + rendered.string.should.equal('http://casper.website/baz'); + }); + + it('should handle hosted urls in a nav context', function () { + rendered = helpers.url.call( + {url: 'http://localhost:82832/qux', label: 'Qux', slug: 'qux', current: true}, + {hash: {absolute: 'true'}}); + should.exist(rendered); + rendered.string.should.equal('http://localhost:82832/qux'); + }); + + it('should handle hosted urls in a nav context with secure', function () { + rendered = helpers.url.call( + { + url: 'http://localhost:82832/qux', label: 'Qux', slug: 'qux', current: true, + secure: true + }, + {hash: {absolute: 'true'}}); + should.exist(rendered); + rendered.string.should.equal('https://localhost:82832/qux'); + }); + + it('should handle hosted https urls in a nav context with secure', function () { + rendered = helpers.url.call( + { + url: 'https://localhost:82832/qux', label: 'Qux', slug: 'qux', current: true, + secure: true + }, + {hash: {absolute: 'true'}}); + should.exist(rendered); + rendered.string.should.equal('https://localhost:82832/qux'); + }); + + it('should handle hosted urls with the wrong protocol in a nav context', function () { + rendered = helpers.url.call( + {url: 'https://localhost:82832/quux', label: 'Quux', slug: 'quux', current: true}, + {hash: {absolute: 'true'}}); + should.exist(rendered); + rendered.string.should.equal('http://localhost:82832/quux'); + }); + + it('should pass through protocol-less URLs regardless of absolute setting', function () { + rendered = helpers.url.call( + {url: '//casper.website/baz', label: 'Baz', slug: 'baz', current: true}, + {hash: {}}); + should.exist(rendered); + rendered.string.should.equal('//casper.website/baz'); + + rendered = helpers.url.call( + {url: '//casper.website/baz', label: 'Baz', slug: 'baz', current: true}, + {hash: {absolute: 'true'}}); + should.exist(rendered); + rendered.string.should.equal('//casper.website/baz'); + }); + + it('should pass through URLs with alternative schemes regardless of absolute setting', function () { + rendered = helpers.url.call( + {url: 'tel:01234567890', label: 'Baz', slug: 'baz', current: true}, + {hash: {}}); + should.exist(rendered); + rendered.string.should.equal('tel:01234567890'); + + rendered = helpers.url.call( + {url: 'mailto:example@ghost.org', label: 'Baz', slug: 'baz', current: true}, + {hash: {}}); + should.exist(rendered); + rendered.string.should.equal('mailto:example@ghost.org'); + + rendered = helpers.url.call( + {url: 'tel:01234567890', label: 'Baz', slug: 'baz', current: true}, + {hash: {absolute: 'true'}}); + should.exist(rendered); + rendered.string.should.equal('tel:01234567890'); + + rendered = helpers.url.call( + {url: 'mailto:example@ghost.org', label: 'Baz', slug: 'baz', current: true}, + {hash: {absolute: 'true'}}); + should.exist(rendered); + rendered.string.should.equal('mailto:example@ghost.org'); + }); + + it('should pass through anchor-only URLs regardless of absolute setting', function () { + rendered = helpers.url.call( + {url: '#thatsthegoodstuff', label: 'Baz', slug: 'baz', current: true}, + {hash: {}}); + should.exist(rendered); + rendered.string.should.equal('#thatsthegoodstuff'); + + rendered = helpers.url.call( + {url: '#thatsthegoodstuff', label: 'Baz', slug: 'baz', current: true}, + {hash: {absolute: 'true'}}); + should.exist(rendered); + rendered.string.should.equal('#thatsthegoodstuff'); + }); + + it('should not HTML-escape URLs', function () { + rendered = helpers.url.call( + {url: '/foo?foo=bar&baz=qux', label: 'Foo', slug: 'foo', current: true}); + should.exist(rendered); + rendered.string.should.equal('/foo?foo=bar&baz=qux'); + }); + + it('should encode URLs', function () { + rendered = helpers.url.call( + {url: '/foo?foo=bar&baz=qux&', label: 'Foo', slug: 'foo', current: true}); + should.exist(rendered); + rendered.string.should.equal('/foo?foo=bar&baz=qux&%3Cscript%3Ealert(%22gotcha%22)%3C/script%3E'); + }); + + it('should not double-encode URLs', function () { + rendered = helpers.url.call( + {url: '/?foo=space%20bar', label: 'Foo', slug: 'foo', current: true}); + should.exist(rendered); + rendered.string.should.equal('/?foo=space%20bar'); + }); }); describe('with subdir', function () { + let sandbox; + + before(function () { + sandbox = sinon.createSandbox(); + urlUtils.stubUrlUtils({url: 'http://localhost:82832/blog'}, sandbox); + }); + + after(function () { + sandbox.restore(); + }); + it('external urls should be retained in a nav context with subdir', function () { - configUtils.set({url: 'http://localhost:82832/blog'}); rendered = helpers.url.call( {url: 'http://casper.website/baz', label: 'Baz', slug: 'baz', current: true}, {hash: {absolute: 'true'}}); @@ -277,8 +291,6 @@ describe('{{url}} helper', function () { }); it('should handle subdir being set in nav context', function () { - configUtils.set({url: 'http://localhost:82832/blog'}); - rendered = helpers.url.call( {url: '/xyzzy', label: 'xyzzy', slug: 'xyzzy', current: true}, {hash: {absolute: 'true'}}); diff --git a/core/test/unit/lib/image/blog-icon_spec.js b/core/test/unit/lib/image/blog-icon_spec.js index 51700f623b..6eee1cf082 100644 --- a/core/test/unit/lib/image/blog-icon_spec.js +++ b/core/test/unit/lib/image/blog-icon_spec.js @@ -4,20 +4,14 @@ var should = require('should'), path = require('path'), rewire = require('rewire'), settingsCache = require('../../../../server/services/settings/cache'), - configUtils = require('../../../utils/configUtils'), - testUtils = require('../../../utils'), - config = configUtils.config, + storageUtils = require('../../../../server/adapters/storage/utils'), + urlUtils = require('../../../utils/urlUtils'), // stuff we are testing blogIcon = rewire('../../../../server/lib/image/blog-icon'); describe('lib/image: blog icon', function () { - before(function () { - configUtils.restore(); - }); - afterEach(function () { - configUtils.restore(); sinon.restore(); rewire('../../../../server/lib/image/blog-icon'); }); @@ -36,21 +30,22 @@ describe('lib/image: blog icon', function () { it('default ico blog icon', function () { blogIcon.getIconUrl().should.eql('/favicon.ico'); }); + describe('absolute URL', function () { it('custom uploaded ico blog icon', function () { - configUtils.set({url: 'http://my-ghost-blog.com/'}); + blogIcon.__set__('urlUtils', urlUtils.getInstance({url: 'http://my-ghost-blog.com/'})); sinon.stub(settingsCache, 'get').withArgs('icon').returns('/content/images/2017/04/my-icon.ico'); blogIcon.getIconUrl(true).should.eql('http://my-ghost-blog.com/favicon.ico'); }); it('custom uploaded png blog icon', function () { - configUtils.set({url: 'http://my-ghost-blog.com/'}); + blogIcon.__set__('urlUtils', urlUtils.getInstance({url: 'http://my-ghost-blog.com/'})); sinon.stub(settingsCache, 'get').withArgs('icon').returns('/content/images/2017/04/my-icon.png'); blogIcon.getIconUrl(true).should.eql('http://my-ghost-blog.com/favicon.png'); }); it('default ico blog icon', function () { - configUtils.set({url: 'http://my-ghost-blog.com/'}); + blogIcon.__set__('urlUtils', urlUtils.getInstance({url: 'http://my-ghost-blog.com/'})); blogIcon.getIconUrl(true).should.eql('http://my-ghost-blog.com/favicon.ico'); }); }); @@ -58,20 +53,20 @@ describe('lib/image: blog icon', function () { describe('with subdirectory', function () { it('custom uploaded ico blog icon', function () { sinon.stub(settingsCache, 'get').withArgs('icon').returns('/content/images/2017/04/my-icon.ico'); - configUtils.set({url: 'http://my-ghost-blog.com/blog'}); + blogIcon.__set__('urlUtils', urlUtils.getInstance({url: 'http://my-ghost-blog.com/blog'})); blogIcon.getIconUrl().should.eql('/blog/favicon.ico'); }); it('custom uploaded png blog icon', function () { sinon.stub(settingsCache, 'get').withArgs('icon').returns('/content/images/2017/04/my-icon.png'); - configUtils.set({url: 'http://my-ghost-blog.com/blog'}); + blogIcon.__set__('urlUtils', urlUtils.getInstance({url: 'http://my-ghost-blog.com/blog'})); blogIcon.getIconUrl().should.eql('/blog/favicon.png'); }); it('default ico blog icon', function () { - configUtils.set({url: 'http://my-ghost-blog.com/blog'}); + blogIcon.__set__('urlUtils', urlUtils.getInstance({url: 'http://my-ghost-blog.com/blog'})); blogIcon.getIconUrl().should.eql('/blog/favicon.ico'); }); }); @@ -80,38 +75,23 @@ describe('lib/image: blog icon', function () { describe('getIconPath', function () { it('custom uploaded ico blog icon', function () { sinon.stub(settingsCache, 'get').withArgs('icon').returns('/content/images/2017/04/my-icon.ico'); - blogIcon.getIconPath().should.eql('/2017/04/my-icon.ico'); + sinon.stub(storageUtils, 'getLocalFileStoragePath'); + blogIcon.getIconPath(); + + storageUtils.getLocalFileStoragePath.calledOnce.should.be.true(); }); it('custom uploaded png blog icon', function () { sinon.stub(settingsCache, 'get').withArgs('icon').returns('/content/images/2017/04/my-icon.png'); - blogIcon.getIconPath().should.eql('/2017/04/my-icon.png'); + sinon.stub(storageUtils, 'getLocalFileStoragePath'); + blogIcon.getIconPath(); + + storageUtils.getLocalFileStoragePath.calledOnce.should.be.true(); }); it('default ico blog icon', function () { blogIcon.getIconPath().should.eql(path.join(__dirname, '../../../../server/public/favicon.ico')); }); - - describe('with subdirectory', function () { - it('custom uploaded ico blog icon', function () { - sinon.stub(settingsCache, 'get').withArgs('icon').returns('/blog/content/images/2017/04/my-icon.ico'); - configUtils.set({url: 'http://my-ghost-blog.com/blog'}); - - blogIcon.getIconPath().should.eql('/2017/04/my-icon.ico'); - }); - - it('custom uploaded png blog icon', function () { - sinon.stub(settingsCache, 'get').withArgs('icon').returns('/blog/content/images/2017/04/my-icon.png'); - configUtils.set({url: 'http://my-ghost-blog.com/blog'}); - - blogIcon.getIconPath().should.eql('/2017/04/my-icon.png'); - }); - - it('default ico blog icon', function () { - configUtils.set({url: 'http://my-ghost-blog.com/blog'}); - blogIcon.getIconPath().should.eql(path.join(__dirname, '../../../../server/public/favicon.ico')); - }); - }); }); describe('isIcoImageType', function () { diff --git a/core/test/unit/lib/image/image-size_spec.js b/core/test/unit/lib/image/image-size_spec.js index 020e615df3..6470e8ac3d 100644 --- a/core/test/unit/lib/image/image-size_spec.js +++ b/core/test/unit/lib/image/image-size_spec.js @@ -4,7 +4,7 @@ const should = require('should'), nock = require('nock'), path = require('path'), configUtils = require('../../../utils/configUtils'), - urlService = require('../../../../server/services/url'), + urlUtils = require('../../../../server/lib/url-utils'), common = require('../../../../server/lib/common'), storage = require('../../../../server/adapters/storage'); @@ -146,9 +146,9 @@ describe('lib/image: image size', function () { width: 1 }; - const urlForStub = sinon.stub(urlService.utils, 'urlFor'); + const urlForStub = sinon.stub(urlUtils, 'urlFor'); urlForStub.withArgs('home').returns('http://myblog.com/'); - const urlGetSubdirStub = sinon.stub(urlService.utils, 'getSubdir'); + const urlGetSubdirStub = sinon.stub(urlUtils, 'getSubdir'); urlGetSubdirStub.returns(''); const requestMock = nock('http://myblog.com') @@ -258,10 +258,10 @@ describe('lib/image: image size', function () { }; storage.getStorage().storagePath = path.join(__dirname, '../../../../test/utils/fixtures/images/'); - const urlForStub = sinon.stub(urlService.utils, 'urlFor'); + const urlForStub = sinon.stub(urlUtils, 'urlFor'); urlForStub.withArgs('image').returns('http://myblog.com/content/images/favicon.png'); urlForStub.withArgs('home').returns('http://myblog.com/'); - const urlGetSubdirStub = sinon.stub(urlService.utils, 'getSubdir'); + const urlGetSubdirStub = sinon.stub(urlUtils, 'getSubdir'); urlGetSubdirStub.returns(''); const requestMock = nock('http://myblog.com') @@ -423,10 +423,10 @@ describe('lib/image: image size', function () { }; storage.getStorage().storagePath = path.join(__dirname, '../../../../test/utils/fixtures/images/'); - const urlForStub = sinon.stub(urlService.utils, 'urlFor'); + const urlForStub = sinon.stub(urlUtils, 'urlFor'); urlForStub.withArgs('image').returns('http://myblog.com/content/images/ghost-logo.png'); urlForStub.withArgs('home').returns('http://myblog.com/'); - const urlGetSubdirStub = sinon.stub(urlService.utils, 'getSubdir'); + const urlGetSubdirStub = sinon.stub(urlUtils, 'getSubdir'); urlGetSubdirStub.returns(''); imageSize.getImageSizeFromStoragePath(url).then(function (res) { @@ -450,10 +450,10 @@ describe('lib/image: image size', function () { }; storage.getStorage().storagePath = path.join(__dirname, '../../../../test/utils/fixtures/images/'); - const urlForStub = sinon.stub(urlService.utils, 'urlFor'); + const urlForStub = sinon.stub(urlUtils, 'urlFor'); urlForStub.withArgs('image').returns('http://myblog.com/blog/content/images/favicon_too_large.png'); urlForStub.withArgs('home').returns('http://myblog.com/'); - const urlGetSubdirStub = sinon.stub(urlService.utils, 'getSubdir'); + const urlGetSubdirStub = sinon.stub(urlUtils, 'getSubdir'); urlGetSubdirStub.returns('/blog'); imageSize.getImageSizeFromStoragePath(url).then(function (res) { @@ -477,10 +477,10 @@ describe('lib/image: image size', function () { }; storage.getStorage().storagePath = path.join(__dirname, '../../../../test/utils/fixtures/images/'); - const urlForStub = sinon.stub(urlService.utils, 'urlFor'); + const urlForStub = sinon.stub(urlUtils, 'urlFor'); urlForStub.withArgs('image').returns('http://myblog.com/content/images/favicon_multi_sizes.ico'); urlForStub.withArgs('home').returns('http://myblog.com/'); - const urlGetSubdirStub = sinon.stub(urlService.utils, 'getSubdir'); + const urlGetSubdirStub = sinon.stub(urlUtils, 'getSubdir'); urlGetSubdirStub.returns(''); imageSize.getImageSizeFromStoragePath(url).then(function (res) { @@ -499,10 +499,10 @@ describe('lib/image: image size', function () { const url = '/content/images/not-existing-image.png'; storage.getStorage().storagePath = path.join(__dirname, '../../../../test/utils/fixtures/images/'); - const urlForStub = sinon.stub(urlService.utils, 'urlFor'); + const urlForStub = sinon.stub(urlUtils, 'urlFor'); urlForStub.withArgs('image').returns('http://myblog.com/content/images/not-existing-image.png'); urlForStub.withArgs('home').returns('http://myblog.com/'); - const urlGetSubdirStub = sinon.stub(urlService.utils, 'getSubdir'); + const urlGetSubdirStub = sinon.stub(urlUtils, 'getSubdir'); urlGetSubdirStub.returns(''); imageSize.getImageSizeFromStoragePath(url) @@ -521,10 +521,10 @@ describe('lib/image: image size', function () { imageSize.__set__('sizeOf', sizeOfStub); storage.getStorage().storagePath = path.join(__dirname, '../../../../test/utils/fixtures/images/'); - const urlForStub = sinon.stub(urlService.utils, 'urlFor'); + const urlForStub = sinon.stub(urlUtils, 'urlFor'); urlForStub.withArgs('image').returns('http://myblog.com/content/images/ghost-logo.pngx'); urlForStub.withArgs('home').returns('http://myblog.com/'); - const urlGetSubdirStub = sinon.stub(urlService.utils, 'getSubdir'); + const urlGetSubdirStub = sinon.stub(urlUtils, 'getSubdir'); urlGetSubdirStub.returns(''); imageSize.getImageSizeFromStoragePath(url) diff --git a/core/test/unit/models/base/index_spec.js b/core/test/unit/models/base/index_spec.js index 0ce0eba4e3..278589ebbf 100644 --- a/core/test/unit/models/base/index_spec.js +++ b/core/test/unit/models/base/index_spec.js @@ -5,7 +5,7 @@ var should = require('should'), security = require('../../../../server/lib/security'), models = require('../../../../server/models'), common = require('../../../../server/lib/common'), - urlService = require('../../../../server/services/url'), + urlUtils = require('../../../../server/lib/url-utils'), testUtils = require('../../../utils'); describe('Models: base', function () { @@ -23,7 +23,7 @@ describe('Models: base', function () { beforeEach(function () { sinon.stub(security.string, 'safe'); - sinon.stub(urlService.utils, 'getProtectedSlugs').returns(['upsi', 'schwupsi']); + sinon.stub(urlUtils, 'getProtectedSlugs').returns(['upsi', 'schwupsi']); Model = sinon.stub(); Model.prototype = { diff --git a/core/test/unit/services/mail/GhostMailer_spec.js b/core/test/unit/services/mail/GhostMailer_spec.js index 11c44389d1..2101e3de66 100644 --- a/core/test/unit/services/mail/GhostMailer_spec.js +++ b/core/test/unit/services/mail/GhostMailer_spec.js @@ -4,6 +4,7 @@ var should = require('should'), mail = require('../../../../server/services/mail'), settingsCache = require('../../../../server/services/settings/cache'), configUtils = require('../../../utils/configUtils'), + urlUtils = require('../../../../server/lib/url-utils'), common = require('../../../../server/lib/common'), mailer, @@ -176,31 +177,42 @@ describe('Mail: Ghostmailer', function () { mailer.from().should.equal('"Blog Title" '); }); - it('should fall back to [blog.title] ', function () { - sinon.stub(settingsCache, 'get').returns('Test'); + describe('should fall back to [blog.title] ', function () { + let mailer; - // Standard domain - configUtils.set({url: 'http://default.com', mail: {from: null}}); + beforeEach(function () { + mailer = new mail.GhostMailer(); + sinon.stub(settingsCache, 'get').returns('Test'); + }); - mailer = new mail.GhostMailer(); + it('standard domain', function () { + sinon.stub(urlUtils, 'urlFor').returns('http://default.com'); + configUtils.set({mail: {from: null}}); - mailer.from().should.equal('"Test" '); + mailer.from().should.equal('"Test" '); + }); - // Trailing slash - configUtils.set({url: 'http://default.com/', mail: {from: null}}); + it('trailing slash', function () { + sinon.stub(urlUtils, 'urlFor').returns('http://default.com/'); + configUtils.set({mail: {from: null}}); - mailer.from().should.equal('"Test" '); + mailer.from().should.equal('"Test" '); + }); - // Strip Port - configUtils.set({url: 'http://default.com:2368/', mail: {from: null}}); - mailer.from().should.equal('"Test" '); + it('strip port', function () { + sinon.stub(urlUtils, 'urlFor').returns('http://default.com:2368/'); + configUtils.set({mail: {from: null}}); + mailer.from().should.equal('"Test" '); + }); - settingsCache.get.restore(); - sinon.stub(settingsCache, 'get').returns('Test"'); + it('Escape title', function () { + settingsCache.get.restore(); + sinon.stub(settingsCache, 'get').returns('Test"'); - // Escape title - configUtils.set({url: 'http://default.com:2368/', mail: {from: null}}); - mailer.from().should.equal('"Test\\"" '); + sinon.stub(urlUtils, 'urlFor').returns('http://default.com:2368/'); + configUtils.set({mail: {from: null}}); + mailer.from().should.equal('"Test\\"" '); + }); }); it('should use mail.from', function () { @@ -239,7 +251,8 @@ describe('Mail: Ghostmailer', function () { }); it('should use default title if not theme title is provided', function () { - configUtils.set({url: 'http://default.com:2368/', mail: {from: null}}); + configUtils.set({mail: {from: null}}); + sinon.stub(urlUtils, 'urlFor').returns('http://default.com:2368/'); mailer = new mail.GhostMailer(); diff --git a/core/test/unit/services/routing/ParentRouter_spec.js b/core/test/unit/services/routing/ParentRouter_spec.js index 706d1d8c13..18a52157d8 100644 --- a/core/test/unit/services/routing/ParentRouter_spec.js +++ b/core/test/unit/services/routing/ParentRouter_spec.js @@ -2,7 +2,7 @@ const should = require('should'), sinon = require('sinon'), configUtils = require('../../../utils/configUtils'), common = require('../../../../server/lib/common'), - urlService = require('../../../../server/services/url'), + urlUtils = require('../../../../server/lib/url-utils'), ParentRouter = require('../../../../server/services/routing/ParentRouter'); describe('UNIT - services/routing/ParentRouter', function () { @@ -12,7 +12,7 @@ describe('UNIT - services/routing/ParentRouter', function () { sinon.stub(common.events, 'emit'); sinon.stub(common.events, 'on'); - sinon.stub(urlService.utils, 'redirect301'); + sinon.stub(urlUtils, 'redirect301'); req = sinon.stub(); req.app = { @@ -76,7 +76,7 @@ describe('UNIT - services/routing/ParentRouter', function () { parentRouter._respectDominantRouter(req, res, next, 'bacon'); next.called.should.eql(false); - urlService.utils.redirect301.withArgs(res, '/channel/').calledOnce.should.be.true(); + urlUtils.redirect301.withArgs(res, '/channel/').calledOnce.should.be.true(); }); it('redirect with query params', function () { @@ -106,7 +106,7 @@ describe('UNIT - services/routing/ParentRouter', function () { parentRouter._respectDominantRouter(req, res, next, 'bacon'); next.called.should.eql(false); - urlService.utils.redirect301.withArgs(res, '/channel/?a=b').calledOnce.should.be.true(); + urlUtils.redirect301.withArgs(res, '/channel/?a=b').calledOnce.should.be.true(); }); it('redirect rss', function () { @@ -136,7 +136,7 @@ describe('UNIT - services/routing/ParentRouter', function () { parentRouter._respectDominantRouter(req, res, next, 'bacon'); next.called.should.eql(false); - urlService.utils.redirect301.withArgs(res, '/channel/rss/').calledOnce.should.be.true(); + urlUtils.redirect301.withArgs(res, '/channel/rss/').calledOnce.should.be.true(); }); it('redirect pagination', function () { @@ -166,11 +166,11 @@ describe('UNIT - services/routing/ParentRouter', function () { parentRouter._respectDominantRouter(req, res, next, 'bacon'); next.called.should.eql(false); - urlService.utils.redirect301.withArgs(res, '/channel/page/2/').calledOnce.should.be.true(); + urlUtils.redirect301.withArgs(res, '/channel/page/2/').calledOnce.should.be.true(); }); it('redirect correctly with subdirectory', function () { - configUtils.set('url', 'http://localhost:7777/blog/'); + sinon.stub(urlUtils, 'createUrl').returns('/blog/channel/'); const parentRouter = new ParentRouter('tag', '/tag/:slug/'); parentRouter.getResourceType = sinon.stub().returns('tags'); @@ -198,7 +198,7 @@ describe('UNIT - services/routing/ParentRouter', function () { parentRouter._respectDominantRouter(req, res, next, 'bacon'); next.called.should.eql(false); - urlService.utils.redirect301.withArgs(res, '/blog/channel/').calledOnce.should.be.true(); + urlUtils.redirect301.withArgs(res, '/blog/channel/').calledOnce.should.be.true(); }); it('no redirect: different data key', function () { @@ -225,7 +225,7 @@ describe('UNIT - services/routing/ParentRouter', function () { parentRouter._respectDominantRouter(req, res, next, 'bacon'); next.called.should.eql(true); - urlService.utils.redirect301.called.should.be.false(); + urlUtils.redirect301.called.should.be.false(); }); it('no redirect: no channel defined', function () { @@ -247,7 +247,7 @@ describe('UNIT - services/routing/ParentRouter', function () { parentRouter._respectDominantRouter(req, res, next, 'bacon'); next.called.should.eql(true); - urlService.utils.redirect301.called.should.be.false(); + urlUtils.redirect301.called.should.be.false(); }); it('redirect primary tag permalink', function () { @@ -277,7 +277,7 @@ describe('UNIT - services/routing/ParentRouter', function () { parentRouter._respectDominantRouter(req, res, next, 'welcome'); next.called.should.eql(false); - urlService.utils.redirect301.withArgs(res, '/route/?x=y').calledOnce.should.be.true(); + urlUtils.redirect301.withArgs(res, '/route/?x=y').calledOnce.should.be.true(); }); }); diff --git a/core/test/unit/services/routing/RSSRouter_spec.js b/core/test/unit/services/routing/RSSRouter_spec.js index 71b14b93a8..e92b274021 100644 --- a/core/test/unit/services/routing/RSSRouter_spec.js +++ b/core/test/unit/services/routing/RSSRouter_spec.js @@ -4,7 +4,7 @@ const should = require('should'), common = require('../../../../server/lib/common'), controllers = require('../../../../server/services/routing/controllers'), RSSRouter = require('../../../../server/services/routing/RSSRouter'), - urlService = require('../../../../server/services/url'); + urlUtils = require('../../../../server/lib/url-utils'); describe('UNIT - services/routing/RSSRouter', function () { describe('instantiate', function () { @@ -15,7 +15,7 @@ describe('UNIT - services/routing/RSSRouter', function () { sinon.spy(RSSRouter.prototype, 'mountRoute'); sinon.spy(RSSRouter.prototype, 'mountRouter'); - sinon.stub(urlService.utils, 'urlJoin'); + sinon.stub(urlUtils, 'urlJoin'); }); afterEach(function () { @@ -24,7 +24,7 @@ describe('UNIT - services/routing/RSSRouter', function () { }); it('default', function () { - urlService.utils.urlJoin.withArgs('/rss/', ':page(\\d+)').returns('/rss-pagination/'); + urlUtils.urlJoin.withArgs('/rss/', ':page(\\d+)').returns('/rss-pagination/'); const rssRouter = new RSSRouter(); @@ -47,7 +47,7 @@ describe('UNIT - services/routing/RSSRouter', function () { it('subdirectory is enabled', function () { configUtils.set('url', 'http://localhost:22222/blog/'); - urlService.utils.urlJoin.withArgs('/rss/', ':page(\\d+)').returns('/rss-pagination/'); + urlUtils.urlJoin.withArgs('/rss/', ':page(\\d+)').returns('/rss-pagination/'); const rssRouter = new RSSRouter(); diff --git a/core/test/unit/services/routing/controllers/entry_spec.js b/core/test/unit/services/routing/controllers/entry_spec.js index 35124412be..cda882a0c4 100644 --- a/core/test/unit/services/routing/controllers/entry_spec.js +++ b/core/test/unit/services/routing/controllers/entry_spec.js @@ -2,6 +2,7 @@ const should = require('should'), sinon = require('sinon'), testUtils = require('../../../../utils'), urlService = require('../../../../../server/services/url'), + urlUtils = require('../../../../../server/lib/url-utils'), controllers = require('../../../../../server/services/routing/controllers'), helpers = require('../../../../../server/services/routing/helpers'), EDITOR_URL = `/editor/post/`; @@ -31,8 +32,8 @@ describe('Unit - services/routing/controllers/entry', function () { return renderStub; }); - sinon.stub(urlService.utils, 'redirectToAdmin'); - sinon.stub(urlService.utils, 'redirect301'); + sinon.stub(urlUtils, 'redirectToAdmin'); + sinon.stub(urlUtils, 'redirect301'); sinon.stub(urlService, 'getResourceById'); req = { @@ -114,7 +115,7 @@ describe('Unit - services/routing/controllers/entry', function () { entry: post }); - urlService.utils.redirectToAdmin.callsFake(function (statusCode, res, editorUrl) { + urlUtils.redirectToAdmin.callsFake(function (statusCode, res, editorUrl) { statusCode.should.eql(302); editorUrl.should.eql(EDITOR_URL + post.id); done(); @@ -164,7 +165,7 @@ describe('Unit - services/routing/controllers/entry', function () { entry: post }); - urlService.utils.redirect301.callsFake(function (res, postUrl) { + urlUtils.redirect301.callsFake(function (res, postUrl) { postUrl.should.eql(post.url); done(); }); @@ -193,7 +194,7 @@ describe('Unit - services/routing/controllers/entry', function () { entry: post }); - urlService.utils.redirect301.callsFake(function (res, postUrl) { + urlUtils.redirect301.callsFake(function (res, postUrl) { postUrl.should.eql(post.url + '?query=true'); done(); }); diff --git a/core/test/unit/services/routing/controllers/preview_spec.js b/core/test/unit/services/routing/controllers/preview_spec.js index dc898bf2a0..c6823148af 100644 --- a/core/test/unit/services/routing/controllers/preview_spec.js +++ b/core/test/unit/services/routing/controllers/preview_spec.js @@ -7,6 +7,8 @@ const should = require('should'), controllers = require('../../../../../server/services/routing/controllers'), helpers = require('../../../../../server/services/routing/helpers'), urlService = require('../../../../../server/services/url'), + urlUtils = require('../../../../../server/lib/url-utils'), + EDITOR_URL = '/editor/post/'; describe('Unit - services/routing/controllers/preview', function () { @@ -55,8 +57,8 @@ describe('Unit - services/routing/controllers/preview', function () { secureStub = sinon.stub(); - sinon.stub(urlService.utils, 'redirectToAdmin'); - sinon.stub(urlService.utils, 'redirect301'); + sinon.stub(urlUtils, 'redirectToAdmin'); + sinon.stub(urlUtils, 'redirect301'); sinon.stub(urlService, 'getUrlByResourceId'); sinon.stub(helpers, 'secure').get(function () { @@ -102,7 +104,7 @@ describe('Unit - services/routing/controllers/preview', function () { post.status = 'published'; urlService.getUrlByResourceId.withArgs(post.id).returns('/something/'); - urlService.utils.redirect301.callsFake(function (res, postUrl) { + urlUtils.redirect301.callsFake(function (res, postUrl) { postUrl.should.eql('/something/'); renderStub.called.should.be.false(); secureStub.called.should.be.false(); @@ -115,7 +117,7 @@ describe('Unit - services/routing/controllers/preview', function () { it('should call redirect if /edit/ (options param) is detected', function (done) { req.params.options = 'edit'; - urlService.utils.redirectToAdmin.callsFake(function (statusCode, res, editorUrl) { + urlUtils.redirectToAdmin.callsFake(function (statusCode, res, editorUrl) { statusCode.should.eql(302); editorUrl.should.eql(EDITOR_URL + post.id); renderStub.called.should.be.false(); @@ -170,8 +172,8 @@ describe('Unit - services/routing/controllers/preview', function () { secureStub = sinon.stub(); - sinon.stub(urlService.utils, 'redirectToAdmin'); - sinon.stub(urlService.utils, 'redirect301'); + sinon.stub(urlUtils, 'redirectToAdmin'); + sinon.stub(urlUtils, 'redirect301'); sinon.stub(urlService, 'getUrlByResourceId'); sinon.stub(helpers, 'secure').get(function () { diff --git a/core/test/unit/services/routing/middlewares/page-param_spec.js b/core/test/unit/services/routing/middlewares/page-param_spec.js index 7a84392af9..4425b7dfd3 100644 --- a/core/test/unit/services/routing/middlewares/page-param_spec.js +++ b/core/test/unit/services/routing/middlewares/page-param_spec.js @@ -2,6 +2,7 @@ const should = require('should'), sinon = require('sinon'), common = require('../../../../../server/lib/common'), urlService = require('../../../../../server/services/url'), + urlUtils = require('../../../../../server/lib/url-utils'), middlewares = require('../../../../../server/services/routing/middlewares'); describe('UNIT: services/routing/middlewares/page-param', function () { @@ -14,7 +15,7 @@ describe('UNIT: services/routing/middlewares/page-param', function () { res = sinon.stub(); next = sinon.stub(); - sinon.stub(urlService.utils, 'redirect301'); + sinon.stub(urlUtils, 'redirect301'); }); afterEach(function () { @@ -27,7 +28,7 @@ describe('UNIT: services/routing/middlewares/page-param', function () { middlewares.pageParam(req, res, next, 2); - urlService.utils.redirect301.called.should.be.false(); + urlUtils.redirect301.called.should.be.false(); next.calledOnce.should.be.true(); req.params.page.should.eql(2); }); @@ -38,7 +39,7 @@ describe('UNIT: services/routing/middlewares/page-param', function () { middlewares.pageParam(req, res, next, 1); - urlService.utils.redirect301.calledOnce.should.be.true(); + urlUtils.redirect301.calledOnce.should.be.true(); next.called.should.be.false(); }); @@ -48,7 +49,7 @@ describe('UNIT: services/routing/middlewares/page-param', function () { middlewares.pageParam(req, res, next, 0); - urlService.utils.redirect301.called.should.be.false(); + urlUtils.redirect301.called.should.be.false(); next.calledOnce.should.be.true(); (next.args[0][0] instanceof common.errors.NotFoundError).should.be.true(); }); @@ -59,7 +60,7 @@ describe('UNIT: services/routing/middlewares/page-param', function () { middlewares.pageParam(req, res, next, 'something'); - urlService.utils.redirect301.called.should.be.false(); + urlUtils.redirect301.called.should.be.false(); next.calledOnce.should.be.true(); (next.args[0][0] instanceof common.errors.NotFoundError).should.be.true(); }); @@ -70,7 +71,7 @@ describe('UNIT: services/routing/middlewares/page-param', function () { middlewares.pageParam(req, res, next, 1); - urlService.utils.redirect301.calledOnce.should.be.true(); + urlUtils.redirect301.calledOnce.should.be.true(); next.called.should.be.false(); }); }); diff --git a/core/test/unit/services/rss/generate-feed_spec.js b/core/test/unit/services/rss/generate-feed_spec.js index 70a368862b..4c25560367 100644 --- a/core/test/unit/services/rss/generate-feed_spec.js +++ b/core/test/unit/services/rss/generate-feed_spec.js @@ -2,7 +2,7 @@ var should = require('should'), sinon = require('sinon'), _ = require('lodash'), testUtils = require('../../../utils'), - configUtils = require('../../../utils/configUtils'), + urlUtils = require('../../../utils/urlUtils'), urlService = require('../../../../server/services/url'), generateFeed = require('../../../../server/services/rss/generate-feed'); @@ -26,13 +26,10 @@ describe('RSS: Generate Feed', function () { }); afterEach(function () { - configUtils.restore(); sinon.restore(); }); beforeEach(function () { - configUtils.set({url: 'http://my-ghost-blog.com'}); - sinon.stub(urlService, 'getUrlByResourceId'); baseUrl = '/rss/'; @@ -43,190 +40,214 @@ describe('RSS: Generate Feed', function () { data.meta = {pagination: {pages: 1}}; }); - it('should get the RSS tags correct', function (done) { - data.posts = []; + describe('without subdirectory', function () { + let sandbox; - generateFeed(baseUrl, data).then(function (xmlData) { - should.exist(xmlData); - - // xml & rss tags - xmlData.should.match(/^<\?xml version="1.0" encoding="UTF-8"\?>/); - xmlData.should.match(/<!\[CDATA\[Test Title\]\]><\/title>/); - xmlData.should.match(/<description><!\[CDATA\[Testing Desc\]\]><\/description>/); - xmlData.should.match(/<link>http:\/\/my-ghost-blog.com\/<\/link>/); - xmlData.should.match(/<image><url>http:\/\/my-ghost-blog.com\/favicon.png<\/url><title>Test Title<\/title><link>http:\/\/my-ghost-blog.com\/<\/link><\/image>/); - xmlData.should.match(/<generator>Ghost 0.6<\/generator>/); - xmlData.should.match(/<lastBuildDate>.*?<\/lastBuildDate>/); - xmlData.should.match(/<atom:link href="http:\/\/my-ghost-blog.com\/rss\/" rel="self"/); - xmlData.should.match(/type="application\/rss\+xml"\/><ttl>60<\/ttl>/); - xmlData.should.match(/<\/channel><\/rss>$/); - - done(); - }).catch(done); - }); - - it('should get the item tags correct', function (done) { - data.posts = posts; - - _.each(data.posts, function (post) { - urlService.getUrlByResourceId.withArgs(post.id, {secure: undefined, absolute: true}).returns('http://my-ghost-blog.com/' + post.slug + '/'); + beforeEach(function () { + sandbox = sinon.createSandbox(); + urlUtils.stubUrlUtils({url: 'http://my-ghost-blog.com'}, sandbox); }); - generateFeed(baseUrl, data).then(function (xmlData) { - should.exist(xmlData); - - // item tags - xmlData.should.match(/<item><title><!\[CDATA\[HTML Ipsum\]\]><\/title>/); - xmlData.should.match(/<description><!\[CDATA\[<h1>HTML Ipsum Presents<\/h1>/); - xmlData.should.match(/<link>http:\/\/my-ghost-blog.com\/html-ipsum\/<\/link>/); - xmlData.should.match(/<image><url>http:\/\/my-ghost-blog.com\/favicon.png<\/url><title>Test Title<\/title><link>http:\/\/my-ghost-blog.com\/<\/link><\/image>/); - xmlData.should.match(/<guid isPermaLink="false">/); - xmlData.should.match(/<\/guid><dc:creator><!\[CDATA\[Joe Bloggs\]\]><\/dc:creator>/); - xmlData.should.match(/<pubDate>Thu, 01 Jan 2015/); - xmlData.should.match(/<content:encoded><!\[CDATA\[<h1>HTML Ipsum Presents<\/h1>/); - xmlData.should.match(/<\/code><\/pre>\]\]><\/content:encoded><\/item>/); - xmlData.should.not.match(/<author>/); - - // basic structure check - var postEnd = '<\/code><\/pre>\]\]><\/content:encoded>', - firstIndex = xmlData.indexOf(postEnd); - - // The first title should be before the first content - xmlData.indexOf('HTML Ipsum').should.be.below(firstIndex); - // The second title should be after the first content - xmlData.indexOf('Ghostly Kitchen Sink').should.be.above(firstIndex); - - done(); - }).catch(done); - }); - - it('should only return visible tags', function (done) { - var postWithTags = posts[2]; - postWithTags.tags = [ - {name: 'public', visibility: 'public'}, - {name: 'internal', visibility: 'internal'}, - {name: 'visibility'} - ]; - - data.posts = [postWithTags]; - - generateFeed(baseUrl, data).then(function (xmlData) { - should.exist(xmlData); - // item tags - xmlData.should.match(/<title><!\[CDATA\[Short and Sweet\]\]>/); - xmlData.should.match(/<description><!\[CDATA\[test stuff/); - xmlData.should.match(/<content:encoded><!\[CDATA\[<!--kg-card-begin: markdown--><h2 id="testing">testing<\/h2>\n/); - xmlData.should.match(/<img src="http:\/\/placekitten.com\/500\/200"/); - xmlData.should.match(/<media:content url="http:\/\/placekitten.com\/500\/200" medium="image"\/>/); - xmlData.should.match(/<category><!\[CDATA\[public\]\]/); - xmlData.should.match(/<category><!\[CDATA\[visibility\]\]/); - xmlData.should.not.match(/<category><!\[CDATA\[internal\]\]/); - done(); - }).catch(done); - }); - - it('should no error if author is somehow not present', function (done) { - data.posts = [_.omit(posts[2], 'primary_author')]; - - generateFeed(baseUrl, data).then(function (xmlData) { - should.exist(xmlData); - - // special/optional tags - xmlData.should.match(/<title><!\[CDATA\[Short and Sweet\]\]>/); - xmlData.should.match(/<description><!\[CDATA\[test stuff/); - xmlData.should.match(/<content:encoded><!\[CDATA\[<!--kg-card-begin: markdown--><h2 id="testing">testing<\/h2>\n/); - xmlData.should.match(/<img src="http:\/\/placekitten.com\/500\/200"/); - xmlData.should.match(/<media:content url="http:\/\/placekitten.com\/500\/200" medium="image"\/>/); - xmlData.should.not.match(/<dc:creator>/); - - done(); - }).catch(done); - }); - - it('should use meta_description and image where available', function (done) { - data.posts = [posts[2]]; - - generateFeed(baseUrl, data).then(function (xmlData) { - should.exist(xmlData); - - // special/optional tags - xmlData.should.match(/<title><!\[CDATA\[Short and Sweet\]\]>/); - xmlData.should.match(/<description><!\[CDATA\[test stuff/); - xmlData.should.match(/<content:encoded><!\[CDATA\[<!--kg-card-begin: markdown--><h2 id="testing">testing<\/h2>\n/); - xmlData.should.match(/<img src="http:\/\/placekitten.com\/500\/200"/); - xmlData.should.match(/<media:content url="http:\/\/placekitten.com\/500\/200" medium="image"\/>/); - - done(); - }).catch(done); - }); - - it('should use excerpt when no meta_description is set', function (done) { - data.posts = [posts[0]]; - - _.each(data.posts, function (post) { - urlService.getUrlByResourceId.withArgs(post.id, {secure: undefined, absolute: true}).returns('http://my-ghost-blog.com/' + post.slug + '/'); + afterEach(function () { + sandbox.restore(); }); - generateFeed(baseUrl, data).then(function (xmlData) { - should.exist(xmlData); + it('should get the RSS tags correct', function (done) { + data.posts = []; - // special/optional tags - xmlData.should.match(/<title><!\[CDATA\[HTML Ipsum\]\]>/); - xmlData.should.match(/<description><!\[CDATA\[This is my custom excerpt!/); + generateFeed(baseUrl, data).then(function (xmlData) { + should.exist(xmlData); - done(); - }).catch(done); + // xml & rss tags + xmlData.should.match(/^<\?xml version="1.0" encoding="UTF-8"\?>/); + xmlData.should.match(/<rss/); + xmlData.should.match(/xmlns:dc="http:\/\/purl.org\/dc\/elements\/1.1\/"/); + xmlData.should.match(/xmlns:content="http:\/\/purl.org\/rss\/1.0\/modules\/content\/"/); + xmlData.should.match(/xmlns:atom="http:\/\/www.w3.org\/2005\/Atom"/); + xmlData.should.match(/version="2.0"/); + xmlData.should.match(/xmlns:media="http:\/\/search.yahoo.com\/mrss\/"/); + + // channel tags + xmlData.should.match(/<channel><title><!\[CDATA\[Test Title\]\]><\/title>/); + xmlData.should.match(/<description><!\[CDATA\[Testing Desc\]\]><\/description>/); + xmlData.should.match(/<link>http:\/\/my-ghost-blog.com\/<\/link>/); + xmlData.should.match(/<image><url>http:\/\/my-ghost-blog.com\/favicon.png<\/url><title>Test Title<\/title><link>http:\/\/my-ghost-blog.com\/<\/link><\/image>/); + xmlData.should.match(/<generator>Ghost 0.6<\/generator>/); + xmlData.should.match(/<lastBuildDate>.*?<\/lastBuildDate>/); + xmlData.should.match(/<atom:link href="http:\/\/my-ghost-blog.com\/rss\/" rel="self"/); + xmlData.should.match(/type="application\/rss\+xml"\/><ttl>60<\/ttl>/); + xmlData.should.match(/<\/channel><\/rss>$/); + + done(); + }).catch(done); + }); + + it('should get the item tags correct', function (done) { + data.posts = posts; + + _.each(data.posts, function (post) { + urlService.getUrlByResourceId.withArgs(post.id, {secure: undefined, absolute: true}).returns('http://my-ghost-blog.com/' + post.slug + '/'); + }); + + generateFeed(baseUrl, data).then(function (xmlData) { + should.exist(xmlData); + + // item tags + xmlData.should.match(/<item><title><!\[CDATA\[HTML Ipsum\]\]><\/title>/); + xmlData.should.match(/<description><!\[CDATA\[<h1>HTML Ipsum Presents<\/h1>/); + xmlData.should.match(/<link>http:\/\/my-ghost-blog.com\/html-ipsum\/<\/link>/); + xmlData.should.match(/<image><url>http:\/\/my-ghost-blog.com\/favicon.png<\/url><title>Test Title<\/title><link>http:\/\/my-ghost-blog.com\/<\/link><\/image>/); + xmlData.should.match(/<guid isPermaLink="false">/); + xmlData.should.match(/<\/guid><dc:creator><!\[CDATA\[Joe Bloggs\]\]><\/dc:creator>/); + xmlData.should.match(/<pubDate>Thu, 01 Jan 2015/); + xmlData.should.match(/<content:encoded><!\[CDATA\[<h1>HTML Ipsum Presents<\/h1>/); + xmlData.should.match(/<\/code><\/pre>\]\]><\/content:encoded><\/item>/); + xmlData.should.not.match(/<author>/); + + // basic structure check + var postEnd = '<\/code><\/pre>\]\]><\/content:encoded>', + firstIndex = xmlData.indexOf(postEnd); + + // The first title should be before the first content + xmlData.indexOf('HTML Ipsum').should.be.below(firstIndex); + // The second title should be after the first content + xmlData.indexOf('Ghostly Kitchen Sink').should.be.above(firstIndex); + + done(); + }).catch(done); + }); + + it('should only return visible tags', function (done) { + var postWithTags = posts[2]; + postWithTags.tags = [ + {name: 'public', visibility: 'public'}, + {name: 'internal', visibility: 'internal'}, + {name: 'visibility'} + ]; + + data.posts = [postWithTags]; + + generateFeed(baseUrl, data).then(function (xmlData) { + should.exist(xmlData); + // item tags + xmlData.should.match(/<title><!\[CDATA\[Short and Sweet\]\]>/); + xmlData.should.match(/<description><!\[CDATA\[test stuff/); + xmlData.should.match(/<content:encoded><!\[CDATA\[<!--kg-card-begin: markdown--><h2 id="testing">testing<\/h2>\n/); + xmlData.should.match(/<img src="http:\/\/placekitten.com\/500\/200"/); + xmlData.should.match(/<media:content url="http:\/\/placekitten.com\/500\/200" medium="image"\/>/); + xmlData.should.match(/<category><!\[CDATA\[public\]\]/); + xmlData.should.match(/<category><!\[CDATA\[visibility\]\]/); + xmlData.should.not.match(/<category><!\[CDATA\[internal\]\]/); + done(); + }).catch(done); + }); + + it('should no error if author is somehow not present', function (done) { + data.posts = [_.omit(posts[2], 'primary_author')]; + + generateFeed(baseUrl, data).then(function (xmlData) { + should.exist(xmlData); + + // special/optional tags + xmlData.should.match(/<title><!\[CDATA\[Short and Sweet\]\]>/); + xmlData.should.match(/<description><!\[CDATA\[test stuff/); + xmlData.should.match(/<content:encoded><!\[CDATA\[<!--kg-card-begin: markdown--><h2 id="testing">testing<\/h2>\n/); + xmlData.should.match(/<img src="http:\/\/placekitten.com\/500\/200"/); + xmlData.should.match(/<media:content url="http:\/\/placekitten.com\/500\/200" medium="image"\/>/); + xmlData.should.not.match(/<dc:creator>/); + + done(); + }).catch(done); + }); + + it('should use meta_description and image where available', function (done) { + data.posts = [posts[2]]; + + generateFeed(baseUrl, data).then(function (xmlData) { + should.exist(xmlData); + + // special/optional tags + xmlData.should.match(/<title><!\[CDATA\[Short and Sweet\]\]>/); + xmlData.should.match(/<description><!\[CDATA\[test stuff/); + xmlData.should.match(/<content:encoded><!\[CDATA\[<!--kg-card-begin: markdown--><h2 id="testing">testing<\/h2>\n/); + xmlData.should.match(/<img src="http:\/\/placekitten.com\/500\/200"/); + xmlData.should.match(/<media:content url="http:\/\/placekitten.com\/500\/200" medium="image"\/>/); + + done(); + }).catch(done); + }); + + it('should use excerpt when no meta_description is set', function (done) { + data.posts = [posts[0]]; + + _.each(data.posts, function (post) { + urlService.getUrlByResourceId.withArgs(post.id, {secure: undefined, absolute: true}).returns('http://my-ghost-blog.com/' + post.slug + '/'); + }); + + generateFeed(baseUrl, data).then(function (xmlData) { + should.exist(xmlData); + + // special/optional tags + xmlData.should.match(/<title><!\[CDATA\[HTML Ipsum\]\]>/); + xmlData.should.match(/<description><!\[CDATA\[This is my custom excerpt!/); + + done(); + }).catch(done); + }); + + it('should process urls correctly', function (done) { + data.posts = [posts[3]]; + + generateFeed(baseUrl, data).then(function (xmlData) { + should.exist(xmlData); + + // anchor URL - <a href="#nowhere" title="Anchor URL"> + xmlData.should.match(/<a href="#nowhere" title="Anchor URL">/); + + // relative URL - <a href="/about#nowhere" title="Relative URL"> + xmlData.should.match(/<a href="http:\/\/my-ghost-blog.com\/about#nowhere" title="Relative URL">/); + + // protocol relative URL - <a href="//somewhere.com/link#nowhere" title="Protocol Relative URL"> + xmlData.should.match(/<a href="\/\/somewhere.com\/link#nowhere" title="Protocol Relative URL">/); + + // absolute URL - <a href="http://somewhere.com/link#nowhere" title="Absolute URL"> + xmlData.should.match(/<a href="http:\/\/somewhere.com\/link#nowhere" title="Absolute URL">/); + + done(); + }).catch(done); + }); }); - it('should process urls correctly', function (done) { - data.posts = [posts[3]]; + describe('with subdirectory', function () { + let sandbox; - generateFeed(baseUrl, data).then(function (xmlData) { - should.exist(xmlData); + beforeEach(function () { + sandbox = sinon.createSandbox(); + urlUtils.stubUrlUtils({url: 'http://my-ghost-blog.com/blog/'}, sandbox); + }); - // anchor URL - <a href="#nowhere" title="Anchor URL"> - xmlData.should.match(/<a href="#nowhere" title="Anchor URL">/); + afterEach(function () { + sandbox.restore(); + }); - // relative URL - <a href="/about#nowhere" title="Relative URL"> - xmlData.should.match(/<a href="http:\/\/my-ghost-blog.com\/about#nowhere" title="Relative URL">/); + it('should process urls correctly with subdirectory', function (done) { + baseUrl = '/blog/rss/'; + data.results = {posts: [posts[3]], meta: {pagination: {pages: 1}}}; - // protocol relative URL - <a href="//somewhere.com/link#nowhere" title="Protocol Relative URL"> - xmlData.should.match(/<a href="\/\/somewhere.com\/link#nowhere" title="Protocol Relative URL">/); + generateFeed(baseUrl, data).then(function (xmlData) { + should.exist(xmlData); - // absolute URL - <a href="http://somewhere.com/link#nowhere" title="Absolute URL"> - xmlData.should.match(/<a href="http:\/\/somewhere.com\/link#nowhere" title="Absolute URL">/); + // anchor URL - <a href="#nowhere" title="Anchor URL"> + xmlData.should.match(/<a href="#nowhere" title="Anchor URL">/); - done(); - }).catch(done); - }); + // relative URL - <a href="/about#nowhere" title="Relative URL"> + xmlData.should.match(/<a href="http:\/\/my-ghost-blog.com\/blog\/about#nowhere" title="Relative URL">/); - it('should process urls correctly with subdirectory', function (done) { - configUtils.set({url: 'http://my-ghost-blog.com/blog/'}); + // absolute URL - <a href="http://somewhere.com/link#nowhere" title="Absolute URL"> + xmlData.should.match(/<a href="http:\/\/somewhere.com\/link#nowhere" title="Absolute URL">/); - baseUrl = '/blog/rss/'; - data.results = {posts: [posts[3]], meta: {pagination: {pages: 1}}}; - - generateFeed(baseUrl, data).then(function (xmlData) { - should.exist(xmlData); - - // anchor URL - <a href="#nowhere" title="Anchor URL"> - xmlData.should.match(/<a href="#nowhere" title="Anchor URL">/); - - // relative URL - <a href="/about#nowhere" title="Relative URL"> - xmlData.should.match(/<a href="http:\/\/my-ghost-blog.com\/blog\/about#nowhere" title="Relative URL">/); - - // absolute URL - <a href="http://somewhere.com/link#nowhere" title="Absolute URL"> - xmlData.should.match(/<a href="http:\/\/somewhere.com\/link#nowhere" title="Absolute URL">/); - - done(); - }).catch(done); + done(); + }).catch(done); + }); }); }); diff --git a/core/test/unit/services/slack_spec.js b/core/test/unit/services/slack_spec.js index e6e2f1669e..eef9c18b29 100644 --- a/core/test/unit/services/slack_spec.js +++ b/core/test/unit/services/slack_spec.js @@ -8,6 +8,7 @@ var should = require('should'), // Stuff we test slack = rewire('../../../server/services/slack'), common = require('../../../server/lib/common'), + imageLib = require('../../../server/lib/image'), urlService = require('../../../server/services/url'), schema = require('../../../server/data/schema').checks, settingsCache = require('../../../server/services/settings/cache'), @@ -107,6 +108,8 @@ describe('Slack', function () { slackReset = slack.__set__('request', makeRequestStub); makeRequestStub.resolves(); + sinon.stub(imageLib.blogIcon, 'getIconUrl').returns('http://myblog.com/favicon.ico'); + configUtils.set('url', 'http://myblog.com'); }); @@ -141,6 +144,7 @@ describe('Slack', function () { requestData.attachments[0].fields[0].value.should.equal('## markdown.'); requestData.attachments[0].should.not.have.property('author_name'); requestData.icon_url.should.equal('http://myblog.com/favicon.ico'); + requestData.username.should.equal('Ghost'); requestData.unfurl_links.should.equal(true); }); @@ -151,8 +155,6 @@ describe('Slack', function () { isPostStub.returns(false); settingsCacheStub.withArgs('slack').returns(slackObjWithUrl); - configUtils.set('url', 'https://myblog.com'); - // execute code ping({message: 'Hi!'}); @@ -167,7 +169,7 @@ describe('Slack', function () { requestUrl.should.equal(slackObjWithUrl[0].url); requestData.text.should.equal('Hi!'); - requestData.icon_url.should.equal('https://myblog.com/favicon.ico'); + requestData.icon_url.should.equal('http://myblog.com/favicon.ico'); requestData.username.should.equal('Ghost'); requestData.unfurl_links.should.equal(true); }); diff --git a/core/test/unit/services/url/UrlGenerator_spec.js b/core/test/unit/services/url/UrlGenerator_spec.js index c2b5fedb0c..83ca583815 100644 --- a/core/test/unit/services/url/UrlGenerator_spec.js +++ b/core/test/unit/services/url/UrlGenerator_spec.js @@ -3,7 +3,7 @@ const Promise = require('bluebird'); const should = require('should'); const nql = require('@nexes/nql'); const sinon = require('sinon'); -const urlUtils = require('../../../../server/services/url/utils'); +const urlUtils = require('../../../../server/lib/url-utils'); const UrlGenerator = require('../../../../server/services/url/UrlGenerator'); describe('Unit: services/url/UrlGenerator', function () { diff --git a/core/test/unit/services/url/utils_spec.js b/core/test/unit/services/url/utils_spec.js deleted file mode 100644 index b7cd6287d8..0000000000 --- a/core/test/unit/services/url/utils_spec.js +++ /dev/null @@ -1,866 +0,0 @@ -const should = require('should'), - sinon = require('sinon'), - _ = require('lodash'), - moment = require('moment-timezone'), - urlService = require('../../../../server/services/url'), - constants = require('../../../../server/lib/constants'), - settingsCache = require('../../../../server/services/settings/cache'), - configUtils = require('../../../utils/configUtils'), - testUtils = require('../../../utils'), - config = configUtils.config; - -describe('Url', function () { - before(function () { - configUtils.restore(); - }); - - afterEach(function () { - configUtils.restore(); - sinon.restore(); - }); - - describe('absoluteToRelative', function () { - it('default', function () { - urlService.utils.absoluteToRelative('http://myblog.com/test/').should.eql('/test/'); - }); - - it('with subdir', function () { - urlService.utils.absoluteToRelative('http://myblog.com/blog/test/').should.eql('/blog/test/'); - }); - - it('with subdir, but request without', function () { - configUtils.set('url', 'http://myblog.com/blog/'); - - urlService.utils.absoluteToRelative('http://myblog.com/blog/test/', {withoutSubdirectory: true}) - .should.eql('/test/'); - }); - - it('with subdir, but request without', function () { - configUtils.set('url', 'http://myblog.com/blog'); - - urlService.utils.absoluteToRelative('http://myblog.com/blog/test/', {withoutSubdirectory: true}) - .should.eql('/test/'); - }); - }); - - describe('relativeToAbsolute', function () { - it('default', function () { - configUtils.set('url', 'http://myblog.com/'); - urlService.utils.relativeToAbsolute('/test/').should.eql('http://myblog.com/test/'); - }); - - it('with subdir', function () { - configUtils.set('url', 'http://myblog.com/blog/'); - urlService.utils.relativeToAbsolute('/test/').should.eql('http://myblog.com/blog/test/'); - }); - - it('should not convert absolute url', function () { - urlService.utils.relativeToAbsolute('http://anotherblog.com/blog/').should.eql('http://anotherblog.com/blog/'); - }); - - it('should not convert absolute url', function () { - urlService.utils.relativeToAbsolute('http://anotherblog.com/blog/').should.eql('http://anotherblog.com/blog/'); - }); - - it('should not convert schemeless url', function () { - urlService.utils.relativeToAbsolute('//anotherblog.com/blog/').should.eql('//anotherblog.com/blog/'); - }); - }); - - describe('getProtectedSlugs', function () { - it('defaults', function () { - urlService.utils.getProtectedSlugs().should.eql(['ghost', 'rss', 'amp']); - }); - - it('url has subdir', function () { - configUtils.set({url: 'http://my-ghost-blog.com/blog'}); - urlService.utils.getProtectedSlugs().should.eql(['ghost', 'rss', 'amp', 'blog']); - }); - }); - - describe('getSubdir', function () { - it('url has no subdir', function () { - urlService.utils.getSubdir().should.eql(''); - }); - - it('url has subdir', function () { - configUtils.set({url: 'http://my-ghost-blog.com/blog'}); - urlService.utils.getSubdir().should.eql('/blog'); - - configUtils.set({url: 'http://my-ghost-blog.com/blog/'}); - urlService.utils.getSubdir().should.eql('/blog'); - - configUtils.set({url: 'http://my-ghost-blog.com/my/blog'}); - urlService.utils.getSubdir().should.eql('/my/blog'); - - configUtils.set({url: 'http://my-ghost-blog.com/my/blog/'}); - urlService.utils.getSubdir().should.eql('/my/blog'); - }); - - it('should not return a slash for subdir', function () { - configUtils.set({url: 'http://my-ghost-blog.com'}); - urlService.utils.getSubdir().should.eql(''); - - configUtils.set({url: 'http://my-ghost-blog.com/'}); - urlService.utils.getSubdir().should.eql(''); - }); - }); - - describe('urlJoin', function () { - it('should deduplicate slashes', function () { - configUtils.set({url: 'http://my-ghost-blog.com/'}); - urlService.utils.urlJoin('/', '/my/', '/blog/').should.equal('/my/blog/'); - urlService.utils.urlJoin('/', '//my/', '/blog/').should.equal('/my/blog/'); - urlService.utils.urlJoin('/', '/', '/').should.equal('/'); - }); - - it('should not deduplicate slashes in protocol', function () { - configUtils.set({url: 'http://my-ghost-blog.com/'}); - urlService.utils.urlJoin('http://myurl.com', '/rss').should.equal('http://myurl.com/rss'); - urlService.utils.urlJoin('https://myurl.com/', '/rss').should.equal('https://myurl.com/rss'); - }); - - it('should permit schemeless protocol', function () { - configUtils.set({url: 'http://my-ghost-blog.com/'}); - urlService.utils.urlJoin('/', '/').should.equal('/'); - urlService.utils.urlJoin('//myurl.com', '/rss').should.equal('//myurl.com/rss'); - urlService.utils.urlJoin('//myurl.com/', '/rss').should.equal('//myurl.com/rss'); - urlService.utils.urlJoin('//myurl.com//', 'rss').should.equal('//myurl.com/rss'); - urlService.utils.urlJoin('', '//myurl.com', 'rss').should.equal('//myurl.com/rss'); - }); - - it('should deduplicate subdir', function () { - configUtils.set({url: 'http://my-ghost-blog.com/blog'}); - urlService.utils.urlJoin('blog', 'blog/about').should.equal('blog/about'); - urlService.utils.urlJoin('blog/', 'blog/about').should.equal('blog/about'); - configUtils.set({url: 'http://my-ghost-blog.com/my/blog'}); - urlService.utils.urlJoin('my/blog', 'my/blog/about').should.equal('my/blog/about'); - urlService.utils.urlJoin('my/blog/', 'my/blog/about').should.equal('my/blog/about'); - }); - - it('should handle subdir matching tld', function () { - configUtils.set({url: 'http://ghost.blog/blog'}); - urlService.utils.urlJoin('ghost.blog/blog', 'ghost/').should.equal('ghost.blog/blog/ghost/'); - urlService.utils.urlJoin('ghost.blog', 'blog', 'ghost/').should.equal('ghost.blog/blog/ghost/'); - }); - }); - - describe('urlFor', function () { - it('should return the home url with no options', function () { - urlService.utils.urlFor().should.equal('/'); - configUtils.set({url: 'http://my-ghost-blog.com/blog'}); - urlService.utils.urlFor().should.equal('/blog/'); - configUtils.set({url: 'http://my-ghost-blog.com/blog/'}); - urlService.utils.urlFor().should.equal('/blog/'); - }); - - it('should return home url when asked for', function () { - var testContext = 'home'; - - configUtils.set({url: 'http://my-ghost-blog.com'}); - urlService.utils.urlFor(testContext).should.equal('/'); - urlService.utils.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/'); - urlService.utils.urlFor(testContext, {secure: true}, true).should.equal('https://my-ghost-blog.com/'); - - configUtils.set({url: 'http://my-ghost-blog.com/'}); - urlService.utils.urlFor(testContext).should.equal('/'); - urlService.utils.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/'); - urlService.utils.urlFor(testContext, {secure: true}, true).should.equal('https://my-ghost-blog.com/'); - - configUtils.set({url: 'http://my-ghost-blog.com/blog'}); - urlService.utils.urlFor(testContext).should.equal('/blog/'); - urlService.utils.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/blog/'); - urlService.utils.urlFor(testContext, {secure: true}, true).should.equal('https://my-ghost-blog.com/blog/'); - - configUtils.set({url: 'http://my-ghost-blog.com/blog/'}); - urlService.utils.urlFor(testContext).should.equal('/blog/'); - urlService.utils.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/blog/'); - urlService.utils.urlFor(testContext, {secure: true}, true).should.equal('https://my-ghost-blog.com/blog/'); - - // Output blog url without trailing slash - configUtils.set({url: 'http://my-ghost-blog.com'}); - urlService.utils.urlFor(testContext).should.equal('/'); - urlService.utils.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/'); - urlService.utils.urlFor(testContext, { - secure: true, - trailingSlash: false - }, true).should.equal('https://my-ghost-blog.com'); - - configUtils.set({url: 'http://my-ghost-blog.com/'}); - urlService.utils.urlFor(testContext).should.equal('/'); - urlService.utils.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/'); - urlService.utils.urlFor(testContext, { - secure: true, - trailingSlash: false - }, true).should.equal('https://my-ghost-blog.com'); - - configUtils.set({url: 'http://my-ghost-blog.com/blog'}); - urlService.utils.urlFor(testContext).should.equal('/blog/'); - urlService.utils.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/blog/'); - urlService.utils.urlFor(testContext, { - secure: true, - trailingSlash: false - }, true).should.equal('https://my-ghost-blog.com/blog'); - - configUtils.set({url: 'http://my-ghost-blog.com/blog/'}); - urlService.utils.urlFor(testContext).should.equal('/blog/'); - urlService.utils.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/blog/'); - urlService.utils.urlFor(testContext, { - secure: true, - trailingSlash: false - }, true).should.equal('https://my-ghost-blog.com/blog'); - }); - - it('should handle weird cases by always returning /', function () { - urlService.utils.urlFor('').should.equal('/'); - urlService.utils.urlFor('post', {}).should.equal('/'); - urlService.utils.urlFor('post', {post: {}}).should.equal('/'); - urlService.utils.urlFor(null).should.equal('/'); - urlService.utils.urlFor(undefined).should.equal('/'); - urlService.utils.urlFor({}).should.equal('/'); - urlService.utils.urlFor({relativeUrl: ''}).should.equal('/'); - urlService.utils.urlFor({relativeUrl: null}).should.equal('/'); - urlService.utils.urlFor({relativeUrl: undefined}).should.equal('/'); - }); - - it('should return url for a random path when asked for', function () { - var testContext = {relativeUrl: '/about/'}; - - configUtils.set({url: 'http://my-ghost-blog.com'}); - urlService.utils.urlFor(testContext).should.equal('/about/'); - urlService.utils.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/about/'); - - configUtils.set({url: 'http://my-ghost-blog.com/blog'}); - urlService.utils.urlFor(testContext).should.equal('/blog/about/'); - urlService.utils.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/blog/about/'); - - testContext.secure = true; - urlService.utils.urlFor(testContext, true).should.equal('https://my-ghost-blog.com/blog/about/'); - - testContext.secure = false; - urlService.utils.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/blog/about/'); - - testContext.secure = false; - configUtils.set({url: 'https://my-ghost-blog.com'}); - urlService.utils.urlFor(testContext, true).should.equal('https://my-ghost-blog.com/about/'); - }); - - it('should deduplicate subdirectories in paths', function () { - var testContext = {relativeUrl: '/blog/about/'}; - - configUtils.set({url: 'http://my-ghost-blog.com'}); - urlService.utils.urlFor(testContext).should.equal('/blog/about/'); - urlService.utils.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/blog/about/'); - - configUtils.set({url: 'http://my-ghost-blog.com/blog'}); - urlService.utils.urlFor(testContext).should.equal('/blog/about/'); - urlService.utils.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/blog/about/'); - - configUtils.set({url: 'http://my-ghost-blog.com/blog/'}); - urlService.utils.urlFor(testContext).should.equal('/blog/about/'); - urlService.utils.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/blog/about/'); - }); - - it('should return url for an image when asked for', function () { - var testContext = 'image', - testData; - - configUtils.set({url: 'http://my-ghost-blog.com'}); - - testData = {image: '/content/images/my-image.jpg'}; - urlService.utils.urlFor(testContext, testData).should.equal('/content/images/my-image.jpg'); - urlService.utils.urlFor(testContext, testData, true).should.equal('http://my-ghost-blog.com/content/images/my-image.jpg'); - - testData = {image: 'http://placekitten.com/500/200'}; - urlService.utils.urlFor(testContext, testData).should.equal('http://placekitten.com/500/200'); - urlService.utils.urlFor(testContext, testData, true).should.equal('http://placekitten.com/500/200'); - - testData = {image: '/blog/content/images/my-image2.jpg'}; - urlService.utils.urlFor(testContext, testData).should.equal('/blog/content/images/my-image2.jpg'); - // We don't make image urls absolute if they don't look like images relative to the image path - urlService.utils.urlFor(testContext, testData, true).should.equal('/blog/content/images/my-image2.jpg'); - - configUtils.set({url: 'http://my-ghost-blog.com/blog/'}); - - testData = {image: '/content/images/my-image3.jpg'}; - urlService.utils.urlFor(testContext, testData).should.equal('/content/images/my-image3.jpg'); - // We don't make image urls absolute if they don't look like images relative to the image path - urlService.utils.urlFor(testContext, testData, true).should.equal('/content/images/my-image3.jpg'); - - testData = {image: '/blog/content/images/my-image4.jpg'}; - urlService.utils.urlFor(testContext, testData).should.equal('/blog/content/images/my-image4.jpg'); - urlService.utils.urlFor(testContext, testData, true).should.equal('http://my-ghost-blog.com/blog/content/images/my-image4.jpg'); - - // Test case for blogs with optional https - - // they may be configured with http url but the actual connection may be over https (#8373) - configUtils.set({url: 'http://my-ghost-blog.com'}); - testData = {image: '/content/images/my-image.jpg', secure: true}; - urlService.utils.urlFor(testContext, testData, true).should.equal('https://my-ghost-blog.com/content/images/my-image.jpg'); - }); - - it('should return a url for a nav item when asked for it', function () { - var testContext = 'nav', - testData; - - configUtils.set({url: 'http://my-ghost-blog.com'}); - - testData = {nav: {url: 'http://my-ghost-blog.com/'}}; - urlService.utils.urlFor(testContext, testData).should.equal('http://my-ghost-blog.com/'); - - testData = {nav: {url: 'http://my-ghost-blog.com/short-and-sweet/'}}; - urlService.utils.urlFor(testContext, testData).should.equal('http://my-ghost-blog.com/short-and-sweet/'); - - testData = {nav: {url: 'http://my-ghost-blog.com//short-and-sweet/'}, secure: true}; - urlService.utils.urlFor(testContext, testData).should.equal('https://my-ghost-blog.com/short-and-sweet/'); - - testData = {nav: {url: 'http://my-ghost-blog.com:3000/'}}; - urlService.utils.urlFor(testContext, testData).should.equal('http://my-ghost-blog.com:3000/'); - - testData = {nav: {url: 'http://my-ghost-blog.com:3000/short-and-sweet/'}}; - urlService.utils.urlFor(testContext, testData).should.equal('http://my-ghost-blog.com:3000/short-and-sweet/'); - - testData = {nav: {url: 'http://sub.my-ghost-blog.com/'}}; - urlService.utils.urlFor(testContext, testData).should.equal('http://sub.my-ghost-blog.com/'); - - testData = {nav: {url: '//sub.my-ghost-blog.com/'}}; - urlService.utils.urlFor(testContext, testData).should.equal('//sub.my-ghost-blog.com/'); - - testData = {nav: {url: 'mailto:sub@my-ghost-blog.com/'}}; - urlService.utils.urlFor(testContext, testData).should.equal('mailto:sub@my-ghost-blog.com/'); - - testData = {nav: {url: '#this-anchor'}}; - urlService.utils.urlFor(testContext, testData).should.equal('#this-anchor'); - - testData = {nav: {url: 'http://some-external-page.com/my-ghost-blog.com'}}; - urlService.utils.urlFor(testContext, testData).should.equal('http://some-external-page.com/my-ghost-blog.com'); - - testData = {nav: {url: 'http://some-external-page.com/stuff-my-ghost-blog.com-around'}}; - urlService.utils.urlFor(testContext, testData).should.equal('http://some-external-page.com/stuff-my-ghost-blog.com-around'); - - testData = {nav: {url: 'mailto:marshmallow@my-ghost-blog.com'}}; - urlService.utils.urlFor(testContext, testData).should.equal('mailto:marshmallow@my-ghost-blog.com'); - - configUtils.set({url: 'http://my-ghost-blog.com/blog'}); - testData = {nav: {url: 'http://my-ghost-blog.com/blog/'}}; - urlService.utils.urlFor(testContext, testData).should.equal('http://my-ghost-blog.com/blog/'); - - testData = {nav: {url: 'http://my-ghost-blog.com/blog/short-and-sweet/'}}; - urlService.utils.urlFor(testContext, testData).should.equal('http://my-ghost-blog.com/blog/short-and-sweet/'); - - testData = {nav: {url: 'http://my-ghost-blog.com:3000/blog/'}}; - urlService.utils.urlFor(testContext, testData).should.equal('http://my-ghost-blog.com:3000/blog/'); - - testData = {nav: {url: 'http://my-ghost-blog.com:3000/blog/short-and-sweet/'}}; - urlService.utils.urlFor(testContext, testData).should.equal('http://my-ghost-blog.com:3000/blog/short-and-sweet/'); - - testData = {nav: {url: 'http://sub.my-ghost-blog.com/blog/'}}; - urlService.utils.urlFor(testContext, testData).should.equal('http://sub.my-ghost-blog.com/blog/'); - - testData = {nav: {url: '//sub.my-ghost-blog.com/blog/'}}; - urlService.utils.urlFor(testContext, testData).should.equal('//sub.my-ghost-blog.com/blog/'); - }); - - it('sitemap: should return other known paths when requested', function () { - configUtils.set({url: 'http://my-ghost-blog.com'}); - urlService.utils.urlFor('sitemap_xsl').should.equal('/sitemap.xsl'); - urlService.utils.urlFor('sitemap_xsl', true).should.equal('http://my-ghost-blog.com/sitemap.xsl'); - }); - - it('admin: relative', function () { - configUtils.set({ - url: 'http://my-ghost-blog.com' - }); - - urlService.utils.urlFor('admin').should.equal('/ghost/'); - }); - - it('admin: url is http', function () { - configUtils.set({ - url: 'http://my-ghost-blog.com' - }); - - urlService.utils.urlFor('admin', true).should.equal('http://my-ghost-blog.com/ghost/'); - }); - - it('admin: custom admin url is set', function () { - configUtils.set({ - url: 'http://my-ghost-blog.com', - admin: { - url: 'https://admin.my-ghost-blog.com' - } - }); - - urlService.utils.urlFor('admin', true).should.equal('https://admin.my-ghost-blog.com/ghost/'); - }); - - it('admin: blog is on subdir', function () { - configUtils.set({ - url: 'http://my-ghost-blog.com/blog' - }); - - urlService.utils.urlFor('admin', true).should.equal('http://my-ghost-blog.com/blog/ghost/'); - }); - - it('admin: blog is on subdir', function () { - configUtils.set({ - url: 'http://my-ghost-blog.com/blog/' - }); - - urlService.utils.urlFor('admin', true).should.equal('http://my-ghost-blog.com/blog/ghost/'); - }); - - it('admin: blog is on subdir', function () { - configUtils.set({ - url: 'http://my-ghost-blog.com/blog' - }); - - urlService.utils.urlFor('admin').should.equal('/blog/ghost/'); - }); - - it('admin: blog is on subdir', function () { - configUtils.set({ - url: 'http://my-ghost-blog.com/blog', - admin: { - url: 'http://something.com' - } - }); - - urlService.utils.urlFor('admin', true).should.equal('http://something.com/blog/ghost/'); - }); - - it('admin: blog is on subdir', function () { - configUtils.set({ - url: 'http://my-ghost-blog.com/blog', - admin: { - url: 'http://something.com/blog' - } - }); - - urlService.utils.urlFor('admin', true).should.equal('http://something.com/blog/ghost/'); - }); - - it('admin: blog is on subdir', function () { - configUtils.set({ - url: 'http://my-ghost-blog.com/blog', - admin: { - url: 'http://something.com/blog/' - } - }); - - urlService.utils.urlFor('admin', true).should.equal('http://something.com/blog/ghost/'); - }); - - it('admin: blog is on subdir', function () { - configUtils.set({ - url: 'http://my-ghost-blog.com/blog/', - admin: { - url: 'http://something.com/blog' - } - }); - - urlService.utils.urlFor('admin', true).should.equal('http://something.com/blog/ghost/'); - }); - - ['v0.1', 'v2'].forEach((apiVersion) => { - function getApiPath(options) { - const baseAPIPath = '/ghost/api/'; - - switch (options.version) { - case 'v0.1': - return `${baseAPIPath}v0.1/`; - case 'v2': - if (options.versionType === 'admin') { - return `${baseAPIPath}v2/admin/`; - } else { - return `${baseAPIPath}v2/content/`; - } - default: - return `${baseAPIPath}v0.1/`; - } - } - - describe(`for api version: ${apiVersion}`, function () { - it('api: should return admin url is set', function () { - configUtils.set({ - url: 'http://my-ghost-blog.com', - admin: { - url: 'https://something.de' - } - }); - - urlService.utils - .urlFor('api', {version: apiVersion, versionType: 'content'}, true) - .should.eql(`https://something.de${getApiPath({version: apiVersion, versionType: 'content'})}`); - }); - - it('api: url has subdir', function () { - configUtils.set({ - url: 'http://my-ghost-blog.com/blog' - }); - - urlService.utils - .urlFor('api', {version: apiVersion, versionType: 'content'}, true) - .should.eql(`http://my-ghost-blog.com/blog${getApiPath({version: apiVersion, versionType: 'content'})}`); - }); - - it('api: relative path is correct', function () { - urlService.utils - .urlFor('api', {version: apiVersion, versionType: 'content'}) - .should.eql(getApiPath({version: apiVersion, versionType: 'content'})); - }); - - it('api: relative path with subdir is correct', function () { - configUtils.set({ - url: 'http://my-ghost-blog.com/blog' - }); - - urlService.utils - .urlFor('api', {version: apiVersion, versionType: 'content'}) - .should.eql(`/blog${getApiPath({version: apiVersion, versionType: 'content'})}`); - }); - - it('api: should return http if config.url is http', function () { - configUtils.set({ - url: 'http://my-ghost-blog.com' - }); - - urlService.utils - .urlFor('api', {version: apiVersion, versionType: 'content'}, true) - .should.eql(`http://my-ghost-blog.com${getApiPath({version: apiVersion, versionType: 'content'})}`); - }); - - it('api: should return https if config.url is https', function () { - configUtils.set({ - url: 'https://my-ghost-blog.com' - }); - - urlService.utils - .urlFor('api', {version: apiVersion, versionType: 'content'}, true) - .should.eql(`https://my-ghost-blog.com${getApiPath({version: apiVersion, versionType: 'content'})}`); - }); - - it('api: with cors, blog url is http: should return no protocol', function () { - configUtils.set({ - url: 'http://my-ghost-blog.com' - }); - - urlService.utils - .urlFor('api', {cors: true, version: apiVersion, versionType: 'content'}, true) - .should.eql(`//my-ghost-blog.com${getApiPath({version: apiVersion, versionType: 'content'})}`); - }); - - it('api: with cors, admin url is http: cors should return no protocol', function () { - configUtils.set({ - url: 'http://my-ghost-blog.com', - admin: { - url: 'http://admin.ghost.example' - } - }); - - urlService.utils - .urlFor('api', {cors: true, version: apiVersion, versionType: 'content'}, true) - .should.eql(`//admin.ghost.example${getApiPath({version: apiVersion, versionType: 'content'})}`); - }); - - it('api: with cors, admin url is https: should return with protocol', function () { - configUtils.set({ - url: 'https://my-ghost-blog.com', - admin: { - url: 'https://admin.ghost.example' - } - }); - - urlService.utils - .urlFor('api', {cors: true, version: apiVersion, versionType: 'content'}, true) - .should.eql(`https://admin.ghost.example${getApiPath({version: apiVersion, versionType: 'content'})}`); - }); - - it('api: with cors, blog url is https: should return with protocol', function () { - configUtils.set({ - url: 'https://my-ghost-blog.com' - }); - - urlService.utils - .urlFor('api', {cors: true, version: apiVersion, versionType: 'content'}, true) - .should.eql(`https://my-ghost-blog.com${getApiPath({version: apiVersion, versionType: 'content'})}`); - }); - - it('api: with stable version, blog url is https: should return stable content api path', function () { - configUtils.set({ - url: 'https://my-ghost-blog.com' - }); - - urlService.utils - .urlFor('api', {cors: true, version: apiVersion, versionType: 'content'}, true) - .should.eql(`https://my-ghost-blog.com${getApiPath({version: apiVersion, versionType: 'content'})}`); - }); - - it('api: with stable version and admin true, blog url is https: should return stable admin api path', function () { - configUtils.set({ - url: 'https://my-ghost-blog.com' - }); - - urlService.utils - .urlFor('api', {cors: true, version: apiVersion, versionType: 'admin'}, true) - .should.eql(`https://my-ghost-blog.com${getApiPath({version: apiVersion, versionType: 'admin'})}`); - }); - - it('api: with just version and no version type returns correct api path', function () { - configUtils.set({ - url: 'https://my-ghost-blog.com' - }); - - urlService.utils - .urlFor('api', {cors: true, version: apiVersion}, true) - .should.eql(`https://my-ghost-blog.com${getApiPath({version: apiVersion})}`); - }); - }); - }); - - it('api: with active version, blog url is https: should return active content api path', function () { - configUtils.set({ - url: 'https://my-ghost-blog.com' - }); - - urlService.utils.urlFor('api', {cors: true, version: "v2", versionType: 'content'}, true).should.eql('https://my-ghost-blog.com/ghost/api/v2/content/'); - }); - - it('api: with active version and admin true, blog url is https: should return active admin api path', function () { - configUtils.set({ - url: 'https://my-ghost-blog.com' - }); - - urlService.utils.urlFor('api', {cors: true, version: "v2", versionType: 'admin'}, true).should.eql('https://my-ghost-blog.com/ghost/api/v2/admin/'); - }); - }); - - describe('replacePermalink', function () { - const localSettingsCache = {}; - - beforeEach(function () { - sinon.stub(settingsCache, 'get').callsFake(function (key) { - return localSettingsCache[key]; - }); - }); - - it('permalink is /:slug/, timezone is default', function () { - var testData = testUtils.DataGenerator.Content.posts[2], - postLink = '/short-and-sweet/'; - - urlService.utils.replacePermalink('/:slug/', testData).should.equal(postLink); - }); - - it('permalink is /:year/:month/:day/:slug/, blog timezone is Los Angeles', function () { - localSettingsCache.active_timezone = 'America/Los_Angeles'; - - var testData = testUtils.DataGenerator.Content.posts[2], - postLink = '/2016/05/17/short-and-sweet/'; - - testData.published_at = new Date('2016-05-18T06:30:00.000Z'); - urlService.utils.replacePermalink('/:year/:month/:day/:slug/', testData).should.equal(postLink); - }); - - it('permalink is /:year/:month/:day/:slug/, blog timezone is Asia Tokyo', function () { - localSettingsCache.active_timezone = 'Asia/Tokyo'; - - var testData = testUtils.DataGenerator.Content.posts[2], - postLink = '/2016/05/18/short-and-sweet/'; - - testData.published_at = new Date('2016-05-18T06:30:00.000Z'); - urlService.utils.replacePermalink('/:year/:month/:day/:slug/', testData).should.equal(postLink); - }); - - it('permalink is /:year/:id/:author/, TZ is LA', function () { - localSettingsCache.active_timezone = 'America/Los_Angeles'; - - var testData = _.merge({}, testUtils.DataGenerator.Content.posts[2], {id: 3}, {primary_author: {slug: 'joe-blog'}}), - postLink = '/2015/3/joe-blog/'; - - testData.published_at = new Date('2016-01-01T00:00:00.000Z'); - urlService.utils.replacePermalink('/:year/:id/:author/', testData).should.equal(postLink); - }); - - it('permalink is /:year/:id:/:author/, TZ is Berlin', function () { - localSettingsCache.active_timezone = 'Europe/Berlin'; - - var testData = _.merge({}, testUtils.DataGenerator.Content.posts[2], {id: 3}, {primary_author: {slug: 'joe-blog'}}), - postLink = '/2016/3/joe-blog/'; - - testData.published_at = new Date('2016-01-01T00:00:00.000Z'); - urlService.utils.replacePermalink('/:year/:id/:author/', testData).should.equal(postLink); - }); - - it('permalink is /:primary_tag/:slug/ and there is a primary_tag', function () { - localSettingsCache.active_timezone = 'Europe/Berlin'; - - var testData = _.merge({}, testUtils.DataGenerator.Content.posts[2], {primary_tag: {slug: 'bitcoin'}}), - postLink = '/bitcoin/short-and-sweet/'; - - testData.published_at = new Date('2016-01-01T00:00:00.000Z'); - urlService.utils.replacePermalink('/:primary_tag/:slug/', testData).should.equal(postLink); - }); - - it('permalink is /:primary_tag/:slug/ and there is NO primary_tag', function () { - localSettingsCache.active_timezone = 'Europe/Berlin'; - - var testData = testUtils.DataGenerator.Content.posts[2], - postLink = '/all/short-and-sweet/'; - - testData.published_at = new Date('2016-01-01T00:00:00.000Z'); - urlService.utils.replacePermalink('/:primary_tag/:slug/', testData).should.equal(postLink); - }); - - it('shows "undefined" for unknown route segments', function () { - localSettingsCache.active_timezone = 'Europe/Berlin'; - - var testData = testUtils.DataGenerator.Content.posts[2], - postLink = '/undefined/short-and-sweet/'; - - testData.published_at = new Date('2016-01-01T00:00:00.000Z'); - urlService.utils.replacePermalink('/:tag/:slug/', testData).should.equal(postLink); - }); - - it('post is not published yet', function () { - localSettingsCache.active_timezone = 'Europe/London'; - - var testData = _.merge(testUtils.DataGenerator.Content.posts[2], {id: 3, published_at: null}), - nowMoment = moment().tz('Europe/London'), - postLink = '/YYYY/MM/DD/short-and-sweet/'; - - postLink = postLink.replace('YYYY', nowMoment.format('YYYY')); - postLink = postLink.replace('MM', nowMoment.format('MM')); - postLink = postLink.replace('DD', nowMoment.format('DD')); - - urlService.utils.replacePermalink('/:year/:month/:day/:slug/', testData).should.equal(postLink); - }); - }); - - describe('isSSL', function () { - it('detects https protocol correctly', function () { - urlService.utils.isSSL('https://my.blog.com').should.be.true(); - urlService.utils.isSSL('http://my.blog.com').should.be.false(); - urlService.utils.isSSL('http://my.https.com').should.be.false(); - }); - }); - - describe('redirects', function () { - it('performs 301 redirect correctly', function (done) { - var res = {}; - - res.set = sinon.spy(); - - res.redirect = function (code, path) { - code.should.equal(301); - path.should.eql('my/awesome/path'); - res.set.calledWith({'Cache-Control': 'public, max-age=' + constants.ONE_YEAR_S}).should.be.true(); - - done(); - }; - - urlService.utils.redirect301(res, 'my/awesome/path'); - }); - - it('performs an admin 301 redirect correctly', function (done) { - var res = {}; - - res.set = sinon.spy(); - - res.redirect = function (code, path) { - code.should.equal(301); - path.should.eql('/ghost/#/my/awesome/path/'); - res.set.calledWith({'Cache-Control': 'public, max-age=' + constants.ONE_YEAR_S}).should.be.true(); - - done(); - }; - - urlService.utils.redirectToAdmin(301, res, '#/my/awesome/path'); - }); - - it('performs an admin 302 redirect correctly', function (done) { - var res = {}; - - res.set = sinon.spy(); - - res.redirect = function (path) { - path.should.eql('/ghost/#/my/awesome/path/'); - res.set.called.should.be.false(); - - done(); - }; - - urlService.utils.redirectToAdmin(302, res, '#/my/awesome/path'); - }); - }); - - describe('make absolute urls ', function () { - var siteUrl = 'http://my-ghost-blog.com', - itemUrl = 'my-awesome-post'; - - beforeEach(function () { - configUtils.set({url: 'http://my-ghost-blog.com'}); - }); - - afterEach(function () { - configUtils.restore(); - }); - - it('[success] does not convert absolute URLs', function () { - var html = '<a href="http://my-ghost-blog.com/content/images" title="Absolute URL">', - result = urlService.utils.makeAbsoluteUrls(html, siteUrl, itemUrl).html(); - - result.should.match(/<a href="http:\/\/my-ghost-blog.com\/content\/images" title="Absolute URL">/); - }); - it('[failure] does not convert protocol relative `//` URLs', function () { - var html = '<a href="//my-ghost-blog.com/content/images" title="Absolute URL">', - result = urlService.utils.makeAbsoluteUrls(html, siteUrl, itemUrl).html(); - - result.should.match(/<a href="\/\/my-ghost-blog.com\/content\/images" title="Absolute URL">/); - }); - it('[failure] does not convert internal links starting with "#"', function () { - var html = '<a href="#jumptosection" title="Table of Content">', - result = urlService.utils.makeAbsoluteUrls(html, siteUrl, itemUrl).html(); - - result.should.match(/<a href="#jumptosection" title="Table of Content">/); - }); - it('[success] converts a relative URL', function () { - var html = '<a href="/about#nowhere" title="Relative URL">', - result = urlService.utils.makeAbsoluteUrls(html, siteUrl, itemUrl).html(); - - result.should.match(/<a href="http:\/\/my-ghost-blog.com\/about#nowhere" title="Relative URL">/); - }); - it('[success] converts a relative URL including subdirectories', function () { - var html = '<a href="/about#nowhere" title="Relative URL">', - result = urlService.utils.makeAbsoluteUrls(html, 'http://my-ghost-blog.com/blog', itemUrl).html(); - - result.should.match(/<a href="http:\/\/my-ghost-blog.com\/blog\/about#nowhere" title="Relative URL">/); - }); - - it('asset urls only', function () { - let html = '<a href="/about" title="Relative URL"><img src="/content/images/1.jpg">'; - let result = urlService.utils.makeAbsoluteUrls(html, siteUrl, itemUrl, {assetsOnly: true}).html(); - - result.should.match(/<img src="http:\/\/my-ghost-blog.com\/content\/images\/1.jpg">/); - result.should.match(/<a href="\/about\" title="Relative URL">/); - - html = '<a href="/content/images/09/01/image.jpg">'; - result = urlService.utils.makeAbsoluteUrls(html, siteUrl, itemUrl, {assetsOnly: true}).html(); - - result.should.match(/<a href="http:\/\/my-ghost-blog.com\/content\/images\/09\/01\/image.jpg">/); - - html = '<a href="/blog/content/images/09/01/image.jpg">'; - result = urlService.utils.makeAbsoluteUrls(html, siteUrl, itemUrl, {assetsOnly: true}).html(); - - result.should.match(/<a href="http:\/\/my-ghost-blog.com\/blog\/content\/images\/09\/01\/image.jpg">/); - - html = '<img src="http://my-ghost-blog.de/content/images/09/01/image.jpg">'; - result = urlService.utils.makeAbsoluteUrls(html, siteUrl, itemUrl, {assetsOnly: true}).html(); - - result.should.match(/<img src="http:\/\/my-ghost-blog.de\/content\/images\/09\/01\/image.jpg">/); - - html = '<img src="http://external.com/image.jpg">'; - result = urlService.utils.makeAbsoluteUrls(html, siteUrl, itemUrl, {assetsOnly: true}).html(); - - result.should.match(/<img src="http:\/\/external.com\/image.jpg">/); - }); - }); -}); diff --git a/core/test/unit/web/middleware/api/cors_spec.js b/core/test/unit/web/middleware/api/cors_spec.js index 0c3170b9e5..69ed3a0dfd 100644 --- a/core/test/unit/web/middleware/api/cors_spec.js +++ b/core/test/unit/web/middleware/api/cors_spec.js @@ -1,7 +1,7 @@ var should = require('should'), sinon = require('sinon'), rewire = require('rewire'), - configUtils = require('../../../../utils/configUtils'), + urlUtils = require('../../../../utils/urlUtils'), cors = rewire('../../../../../server/web/shared/middlewares/api/cors'); describe('cors', function () { @@ -31,7 +31,6 @@ describe('cors', function () { afterEach(function () { sinon.restore(); - configUtils.restore(); cors = rewire('../../../../../server/web/shared/middlewares/api/cors'); }); @@ -142,9 +141,8 @@ describe('cors', function () { it('should be enabled if the origin matches config.url', function (done) { var origin = 'http://my.blog'; - configUtils.set({ - url: origin - }); + + cors.__set__('urlUtils', urlUtils.getInstance({url: origin})); req.get = sinon.stub().withArgs('origin').returns(origin); res.get = sinon.stub().withArgs('origin').returns(origin); @@ -161,12 +159,10 @@ describe('cors', function () { it('should be enabled if the origin matches config.url', function (done) { var origin = 'http://admin:2222'; - configUtils.set({ + cors.__set__('urlUtils', urlUtils.getInstance({ url: 'https://blog', - admin: { - url: origin - } - }); + adminUrl: origin, + })); req.get = sinon.stub().withArgs('origin').returns(origin); res.get = sinon.stub().withArgs('origin').returns(origin); diff --git a/core/test/unit/web/middleware/url-redirects_spec.js b/core/test/unit/web/middleware/url-redirects_spec.js index 631ec2e375..c6778a1662 100644 --- a/core/test/unit/web/middleware/url-redirects_spec.js +++ b/core/test/unit/web/middleware/url-redirects_spec.js @@ -1,7 +1,7 @@ var should = require('should'), sinon = require('sinon'), rewire = require('rewire'), - configUtils = require('../../../utils/configUtils'), + urlUtils = require('../../../utils/urlUtils'), urlRedirects = rewire('../../../../server/web/shared/middlewares/url-redirects'), {adminRedirect} = urlRedirects, getAdminRedirectUrl = urlRedirects.__get__('_private.getAdminRedirectUrl'), @@ -27,7 +27,6 @@ describe('UNIT: url redirects', function () { afterEach(function () { sinon.restore(); - configUtils.restore(); host = null; }); @@ -40,9 +39,7 @@ describe('UNIT: url redirects', function () { }); it('urlRedirects passes getAdminRedirectUrl method when iAdmin flag is not set', function () { - configUtils.set({ - url: 'https://default.com:2368/' - }); + urlRedirects.__set__('urlUtils', urlUtils.getInstance({url: 'https://default.com:2368/'})); urlRedirects(req, res, next); @@ -51,9 +48,7 @@ describe('UNIT: url redirects', function () { it('urlRedirects passes getAdminRedirectUrl method when iAdmin flag present', function () { res.isAdmin = true; - configUtils.set({ - url: 'https://default.com:2368/' - }); + urlRedirects.__set__('urlUtils', urlUtils.getInstance({url: 'https://default.com:2368/'})); urlRedirects(req, res, next); @@ -61,9 +56,7 @@ describe('UNIT: url redirects', function () { }); it('adminRedirect passes getAdminRedirectUrl', function () { - configUtils.set({ - url: 'https://default.com:2368/' - }); + urlRedirects.__set__('urlUtils', urlUtils.getInstance({url: 'https://default.com:2368/'})); adminRedirect(req, res, next); @@ -73,9 +66,9 @@ describe('UNIT: url redirects', function () { describe('expect redirect', function () { it('blog is https, request is http', function (done) { - configUtils.set({ + urlRedirects.__set__('urlUtils', urlUtils.getInstance({ url: 'https://default.com:2368/' - }); + })); host = 'default.com:2368'; @@ -89,10 +82,9 @@ describe('UNIT: url redirects', function () { }); it('blog host is !== request host', function (done) { - configUtils.set({ + urlRedirects.__set__('urlUtils', urlUtils.getInstance({ url: 'https://default.com' - }); - + })); host = 'localhost:2368'; req.originalUrl = '/'; @@ -106,12 +98,10 @@ describe('UNIT: url redirects', function () { describe(`admin redirects`, function () { it('url and admin url are equal, but protocol is different, request is http', function (done) { - configUtils.set({ + urlRedirects.__set__('urlUtils', urlUtils.getInstance({ url: 'http://default.com:2368', - admin: { - url: 'https://default.com:2368' - } - }); + adminUrl: 'https://default.com:2368' + })); host = 'default.com:2368'; @@ -124,12 +114,10 @@ describe('UNIT: url redirects', function () { }); it('url and admin url are different, request is http', function (done) { - configUtils.set({ + urlRedirects.__set__('urlUtils', urlUtils.getInstance({ url: 'http://default.com:2368', - admin: { - url: 'https://admin.default.com:2368' - } - }); + adminUrl: 'https://admin.default.com:2368' + })); host = 'default.com:2368'; @@ -142,12 +130,10 @@ describe('UNIT: url redirects', function () { }); it('subdirectory', function (done) { - configUtils.set({ + urlRedirects.__set__('urlUtils', urlUtils.getInstance({ url: 'http://default.com:2368/blog', - admin: { - url: 'https://admin.default.com:2368' - } - }); + adminUrl: 'https://admin.default.com:2368' + })); host = 'default.com:2368'; @@ -167,12 +153,10 @@ describe('UNIT: url redirects', function () { }); it('keeps query', function (done) { - configUtils.set({ + urlRedirects.__set__('urlUtils', urlUtils.getInstance({ url: 'http://default.com:2368', - admin: { - url: 'https://admin.default.com:2368' - } - }); + adminUrl: 'https://admin.default.com:2368' + })); host = 'default.com:2368'; @@ -189,12 +173,10 @@ describe('UNIT: url redirects', function () { }); it('original url has search params', function (done) { - configUtils.set({ + urlRedirects.__set__('urlUtils', urlUtils.getInstance({ url: 'http://default.com:2368', - admin: { - url: 'https://admin.default.com:2368' - } - }); + adminUrl: 'https://admin.default.com:2368' + })); host = 'default.com:2368'; @@ -211,12 +193,10 @@ describe('UNIT: url redirects', function () { }); it('ensure redirect loop won\'t happen', function (done) { - configUtils.set({ + urlRedirects.__set__('urlUtils', urlUtils.getInstance({ url: 'http://default.com:2368', - admin: { - url: 'https://default.com:2368' - } - }); + adminUrl: 'https://default.com:2368' + })); host = 'default.com:2368'; @@ -240,9 +220,9 @@ describe('UNIT: url redirects', function () { describe('expect no redirect', function () { it('blog is http, request is http', function (done) { - configUtils.set({ + urlRedirects.__set__('urlUtils', urlUtils.getInstance({ url: 'http://default.com:2368/' - }); + })); host = 'default.com:2368'; @@ -256,9 +236,9 @@ describe('UNIT: url redirects', function () { }); it('blog is http, request is https', function (done) { - configUtils.set({ + urlRedirects.__set__('urlUtils', urlUtils.getInstance({ url: 'http://default.com:2368/' - }); + })); host = 'default.com:2368'; @@ -272,9 +252,9 @@ describe('UNIT: url redirects', function () { }); it('blog is http, request is https (trailing slash is missing)', function (done) { - configUtils.set({ + urlRedirects.__set__('urlUtils', urlUtils.getInstance({ url: 'http://default.com:2368/' - }); + })); host = 'default.com:2368/'; @@ -288,9 +268,9 @@ describe('UNIT: url redirects', function () { }); it('blog is https, request is https', function (done) { - configUtils.set({ + urlRedirects.__set__('urlUtils', urlUtils.getInstance({ url: 'https://default.com:2368/' - }); + })); host = 'default.com:2368'; @@ -305,9 +285,9 @@ describe('UNIT: url redirects', function () { }); it('blog host is !== request host', function (done) { - configUtils.set({ + urlRedirects.__set__('urlUtils', urlUtils.getInstance({ url: 'https://default.com' - }); + })); host = 'localhost:2368'; @@ -322,9 +302,9 @@ describe('UNIT: url redirects', function () { describe(`admin redirects`, function () { it('admin is blog url and http, requester is http', function (done) { - configUtils.set({ + urlRedirects.__set__('urlUtils', urlUtils.getInstance({ url: 'http://default.com:2368' - }); + })); host = 'default.com:2368'; @@ -337,9 +317,9 @@ describe('UNIT: url redirects', function () { }); it('admin request, no custom admin.url configured', function (done) { - configUtils.set({ + urlRedirects.__set__('urlUtils', urlUtils.getInstance({ url: 'http://default.com:2368' - }); + })); host = 'localhost:2368'; @@ -352,12 +332,10 @@ describe('UNIT: url redirects', function () { }); it('url and admin url are different, protocol is different, request is not secure', function (done) { - configUtils.set({ + urlRedirects.__set__('urlUtils', urlUtils.getInstance({ url: 'http://blog.ghost.org', - admin: { - url: 'http://something.com' - } - }); + adminUrl: 'http://something.com' + })); host = 'something.com'; req.secure = false; @@ -371,12 +349,10 @@ describe('UNIT: url redirects', function () { }); it('url and admin url are different, protocol is different, request is secure', function (done) { - configUtils.set({ + urlRedirects.__set__('urlUtils', urlUtils.getInstance({ url: 'http://blog.ghost.org', - admin: { - url: 'http://something.com' - } - }); + adminUrl: 'http://something.com' + })); host = 'something.com'; req.secure = true; diff --git a/core/test/utils/index.js b/core/test/utils/index.js index 2066e44629..bcecd83216 100644 --- a/core/test/utils/index.js +++ b/core/test/utils/index.js @@ -18,6 +18,7 @@ var Promise = require('bluebird'), schema = require('../../server/data/schema').tables, schemaTables = Object.keys(schema), models = require('../../server/models'), + urlUtils = require('../../server/lib/url-utils'), urlService = require('../../server/services/url'), routingService = require('../../server/services/routing'), settingsService = require('../../server/services/settings'), @@ -969,7 +970,7 @@ startGhost = function startGhost(options) { if (options.subdir) { parentApp = express(); - parentApp.use(urlService.utils.getSubdir(), ghostServer.rootApp); + parentApp.use(urlUtils.getSubdir(), ghostServer.rootApp); return ghostServer.start(parentApp); } diff --git a/core/test/utils/urlUtils.js b/core/test/utils/urlUtils.js new file mode 100644 index 0000000000..a790e4dbbf --- /dev/null +++ b/core/test/utils/urlUtils.js @@ -0,0 +1,50 @@ +const _ = require('lodash'); +const sinon = require('sinon'); +const UrlUtils = require('@tryghost/url-utils'); +const config = require('../../server/config'); +const urlUtils = require('../../server/lib/url-utils'); + +const defaultSandbox = sinon.createSandbox(); + +const getInstance = (options) => { + const opts = { + url: options.url, + adminUrl: options.adminUrl, + apiVersions: options.apiVersions, + slugs: options.slugs, + redirectCacheMaxAge: options.redirectCacheMaxAge, + baseApiPath: '/ghost/api' + }; + + return new UrlUtils(opts); +}; + +const stubUrlUtils = (options, sandbox) => { + const stubInstance = getInstance(options); + + Object.keys(urlUtils).forEach((key) => { + sandbox.stub(urlUtils, key).callsFake(stubInstance[key]); + }); +}; + +// Method for regressions tests must be used with restore method +const stubUrlUtilsFromConfig = () => { + const options = { + url: config.get('url'), + adminUrl: config.get('admin:url'), + apiVersions: config.get('api:versions'), + slugs: config.get('slugs').protected, + redirectCacheMaxAge: config.get('caching:301:maxAge'), + baseApiPath: '/ghost/api' + }; + stubUrlUtils(options, defaultSandbox); +}; + +const restore = () => { + defaultSandbox.restore(); +}; + +module.exports.stubUrlUtils = stubUrlUtils; +module.exports.stubUrlUtilsFromConfig = stubUrlUtilsFromConfig; +module.exports.restore = restore; +module.exports.getInstance = getInstance; diff --git a/package.json b/package.json index e34b4094a5..fb12a94fe6 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "@tryghost/members-theme-bindings": "^0.1.0", "@tryghost/social-urls": "0.1.0", "@tryghost/string": "^0.1.3", + "@tryghost/url-utils": "0.1.2", "ajv": "6.8.1", "amperize": "0.5.2", "analytics-node": "3.3.0", diff --git a/yarn.lock b/yarn.lock index c0e3318f9d..881e1ed899 100644 --- a/yarn.lock +++ b/yarn.lock @@ -240,6 +240,16 @@ dependencies: unidecode "^0.1.8" +"@tryghost/url-utils@0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@tryghost/url-utils/-/url-utils-0.1.2.tgz#8224a80d69a78a8b178e14219f64ec0d32676166" + integrity sha512-kj2kHzkAmwL3trVaL90C/c6DdVYAtJ3KdMRB7V1JkBKL6VJJUuoBte2AR9FBbR429vRngoySzCt8jyYOfuQsKQ== + dependencies: + cheerio "0.22.0" + lodash "4.17.11" + moment "2.24.0" + moment-timezone "0.5.23" + "@types/bluebird@^3.5.25": version "3.5.25" resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.25.tgz#59188b871208092e37767e4b3d80c3b3eaae43bd"