Migrated to use url-utils from Ghost-SDK (#10787)

closes #10773

- The refactoring is a substitute for `urlService.utils` used previously throughout the codebase and now extracted into the separate module in Ghost-SDK
- Added url-utils stubbing utility for test suites
- Some tests had to be refactored to avoid double mocks (when url's are being reset inside of rested 'describe' groups)
This commit is contained in:
Naz Gargol 2019-06-18 15:13:55 +02:00 committed by GitHub
parent a63d29f859
commit abda6e6338
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
118 changed files with 1509 additions and 2713 deletions

View File

@ -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

View File

@ -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'), '/');

View File

@ -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)) {

View File

@ -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: {

View File

@ -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')

View File

@ -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);

View File

@ -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, '/');
}
}

View File

@ -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'});

View File

@ -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'});

View File

@ -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 {

View File

@ -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 {

View File

@ -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
};
}

View File

@ -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);
});
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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) => {

View File

@ -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({

View File

@ -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')

View File

@ -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') +

View File

@ -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;

View File

@ -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);
};

View File

@ -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;

View File

@ -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;
}

View File

@ -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!

View File

@ -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;
}

View File

@ -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

View File

@ -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, '/');

View File

@ -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;

View File

@ -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'),

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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',

View File

@ -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)) {

View File

@ -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 {

View File

@ -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 '<?xml version="1.0" encoding="UTF-8"?>' +
'<?xml-stylesheet type="text/xsl" href="' + baseUrl + '"?>';

View File

@ -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'));
}

View File

@ -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);

View File

@ -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')
};

View File

@ -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')
})

View File

@ -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'});
}
}
};

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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'))
}
});
}

View File

@ -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];
};

View File

@ -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);

View File

@ -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
});

View File

@ -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() {

View File

@ -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);
}
/**

View File

@ -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);
}
/**

View File

@ -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)
);
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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));
}
/**

View File

@ -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
}));

View File

@ -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

View File

@ -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({

View File

@ -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/',

View File

@ -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',

View File

@ -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, {

View File

@ -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',

View File

@ -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.

View File

@ -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');
/**

View File

@ -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;

View File

@ -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');

View File

@ -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`);
});
}

View File

@ -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();

View File

@ -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;
};
};

View File

@ -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);
};
};

View File

@ -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);

View File

@ -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) => {

View File

@ -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 = {

View File

@ -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();

View File

@ -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');

View File

@ -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;

View File

@ -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();
});

View File

@ -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')

View File

@ -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')

View File

@ -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 () {

View File

@ -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 () {

View File

@ -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

View File

@ -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);

View File

@ -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');
});

View File

@ -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');
});
});
});

View File

@ -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',

View File

@ -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) {

View File

@ -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 = [{

View File

@ -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();
});
});

View File

@ -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');

View File

@ -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();
});
});

View File

@ -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 () {

View File

@ -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();
});

View File

@ -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',

View File

@ -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();
});
});

View File

@ -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(/<link rel="shortcut icon" href="\/blog\/favicon.png" type="image\/png" \/>/);
rendered.string.should.match(/<link rel="canonical" href="http:\/\/localhost:65530\/blog\/" \/>/);
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/localhost:65530\/blog\/rss\/" \/>/);
rendered.string.should.not.match(/<meta name="description" /);
routing.registry.getRssUrl.returns('http://localhost:65530/blog/rss/');
});
done();
}).catch(done);
});
after(function () {
sandbox.restore();
routing.registry.getRssUrl.returns('http://localhost:65530/rss/');
});
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(/<link rel="shortcut icon" href="\/blog\/favicon.png" type="image\/png" \/>/);
rendered.string.should.match(/<link rel="canonical" href="http:\/\/localhost:65530\/blog\/" \/>/);
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/localhost:65530\/blog\/rss\/" \/>/);
rendered.string.should.not.match(/<meta name="description" /);
done();
}).catch(done);
});
});
describe('with changed origin in config file', function () {
let sandbox;
before(function () {
sandbox = sinon.createSandbox();
settingsCache.get.withArgs('icon').returns('/content/images/favicon.png');
configUtils.set({
url: 'http://localhost:65530/blog/',
referrerPolicy: 'origin'
});
testUrlUtils.stubUrlUtils({url: 'http://localhost:65530/blog'}, sandbox);
});
after(function () {
sandbox.restore();
});
it('contains the changed origin', function (done) {
@ -1298,15 +1319,23 @@ describe('{{ghost_head}} helper', function () {
});
describe('with useStructuredData is set to false in config file', function () {
let sandbox;
before(function () {
sandbox = sinon.createSandbox();
settingsCache.get.withArgs('icon').returns('/content/images/favicon.png');
configUtils.set({
url: 'http://localhost:65530/',
privacy: {
useStructuredData: false
}
});
testUrlUtils.stubUrlUtils({url: 'http://localhost:65530/'}, sandbox);
});
after(function () {
sandbox.restore();
});
it('does not return structured data', function (done) {
@ -1338,13 +1367,18 @@ describe('{{ghost_head}} helper', function () {
});
describe('with Code Injection', function () {
let sandbox;
before(function () {
sandbox = sinon.createSandbox();
settingsCache.get.withArgs('icon').returns('/content/images/favicon.png');
settingsCache.get.withArgs('ghost_head').returns('<style>body {background: red;}</style>');
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);
});

View File

@ -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: {

View File

@ -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&<script>alert("gotcha")</script>', 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&<script>alert("gotcha")</script>', 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'}});

View File

@ -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 () {

Some files were not shown because too many files have changed in this diff Show More