diff --git a/core/client/assets/lib/uploader.js b/core/client/assets/lib/uploader.js index 8c12cd84b7..754ef28ce3 100644 --- a/core/client/assets/lib/uploader.js +++ b/core/client/assets/lib/uploader.js @@ -47,7 +47,7 @@ .attr({'src': '', "width": 'auto', "height": 'auto'}); $progress.animate({"opacity": 0}, 250, function () { - $dropzone.find('span.media').after(''); + $dropzone.find('span.media').after(''); if (!settings.editor) {$progress.find('.fileupload-loading').css({"top": "56px"}); } }); $dropzone.trigger("uploadsuccess", [result]); @@ -62,7 +62,7 @@ var self = this; $dropzone.find('.js-fileupload').fileupload().fileupload("option", { - url: Ghost.paths.ghostRoot + '/ghost/upload/', + url: Ghost.paths.subdir + '/ghost/upload/', headers: { 'X-CSRF-Token': $("meta[name='csrf-param']").attr('content') }, diff --git a/core/client/helpers/index.js b/core/client/helpers/index.js index b44239fe54..b27fa282fc 100644 --- a/core/client/helpers/index.js +++ b/core/client/helpers/index.js @@ -1,5 +1,4 @@ -/*globals Handlebars, moment -*/ +/*globals Handlebars, moment, Ghost */ (function () { 'use strict'; Handlebars.registerHelper('date', function (context, options) { @@ -29,4 +28,8 @@ } return date; }); + + Handlebars.registerHelper('url', function () { + return Ghost.paths.subdir; + }); }()); diff --git a/core/client/init.js b/core/client/init.js index 2aadb42124..28713b6af9 100644 --- a/core/client/init.js +++ b/core/client/init.js @@ -4,11 +4,11 @@ function ghostPaths() { var path = window.location.pathname, - root = path.substr(0, path.search('/ghost/')); + subdir = path.substr(0, path.search('/ghost/')); return { - ghostRoot: root, - apiRoot: root + '/ghost/api/v0.1' + subdir: subdir, + apiRoot: subdir + '/ghost/api/v0.1' }; } @@ -61,7 +61,7 @@ Backbone.history.start({ pushState: true, hashChange: false, - root: Ghost.paths.ghostRoot + '/ghost' + root: Ghost.paths.subdir + '/ghost' }); }; diff --git a/core/client/tpl/login.hbs b/core/client/tpl/login.hbs index c09f6ace74..896e1106d1 100644 --- a/core/client/tpl/login.hbs +++ b/core/client/tpl/login.hbs @@ -7,6 +7,6 @@
- Forgotten password?{{! • Register new user}} + Forgotten password?
diff --git a/core/client/tpl/preview.hbs b/core/client/tpl/preview.hbs index 55945630ff..75a7c43fad 100644 --- a/core/client/tpl/preview.hbs +++ b/core/client/tpl/preview.hbs @@ -53,7 +53,7 @@

You Haven't Written Any Posts Yet!

-
+
{{/unless}} diff --git a/core/client/views/blog.js b/core/client/views/blog.js index da0e358fce..bdc8024a53 100644 --- a/core/client/views/blog.js +++ b/core/client/views/blog.js @@ -213,7 +213,7 @@ e.preventDefault(); // for now this will disable "open in new tab", but when we have a Router implemented // it can go back to being a normal link to '#/ghost/editor/X' - window.location = Ghost.paths.ghostRoot + '/ghost/editor/' + this.model.get('id') + '/'; + window.location = Ghost.paths.subdir + '/ghost/editor/' + this.model.get('id') + '/'; }, toggleFeatured: function (e) { diff --git a/core/client/views/login.js b/core/client/views/login.js index ac8eba668d..4a5676f7ce 100644 --- a/core/client/views/login.js +++ b/core/client/views/login.js @@ -35,7 +35,7 @@ Ghost.Validate.handleErrors(); } else { $.ajax({ - url: Ghost.paths.ghostRoot + '/ghost/signin/', + url: Ghost.paths.subdir + '/ghost/signin/', type: 'POST', headers: { 'X-CSRF-Token': $("meta[name='csrf-param']").attr('content') @@ -100,7 +100,7 @@ Ghost.Validate.handleErrors(); } else { $.ajax({ - url: Ghost.paths.ghostRoot + '/ghost/signup/', + url: Ghost.paths.subdir + '/ghost/signup/', type: 'POST', headers: { 'X-CSRF-Token': $("meta[name='csrf-param']").attr('content') @@ -157,7 +157,7 @@ Ghost.Validate.handleErrors(); } else { $.ajax({ - url: Ghost.paths.ghostRoot + '/ghost/forgotten/', + url: Ghost.paths.subdir + '/ghost/forgotten/', type: 'POST', headers: { 'X-CSRF-Token': $("meta[name='csrf-param']").attr('content') @@ -224,7 +224,7 @@ this.$('input, button').prop('disabled', true); $.ajax({ - url: Ghost.paths.ghostRoot + '/ghost/reset/' + this.token + '/', + url: Ghost.paths.subdir + '/ghost/reset/' + this.token + '/', type: 'POST', headers: { 'X-CSRF-Token': $("meta[name='csrf-param']").attr('content') diff --git a/core/client/views/post-settings.js b/core/client/views/post-settings.js index f3f8f02b5b..5fcf43c5f0 100644 --- a/core/client/views/post-settings.js +++ b/core/client/views/post-settings.js @@ -221,7 +221,7 @@ }).then(function () { // Redirect to content screen if deleting post from editor. if (window.location.pathname.indexOf('editor') > -1) { - window.location = Ghost.paths.ghostRoot + '/ghost/content/'; + window.location = Ghost.paths.subdir + '/ghost/content/'; } Ghost.notifications.addItem({ type: 'success', diff --git a/core/client/views/settings.js b/core/client/views/settings.js index 9f998852ec..7872b21e4a 100644 --- a/core/client/views/settings.js +++ b/core/client/views/settings.js @@ -383,7 +383,7 @@ } else { $.ajax({ - url: '/ghost/changepw/', + url: Ghost.paths.subdir + '/ghost/changepw/', type: 'POST', headers: { 'X-CSRF-Token': $("meta[name='csrf-param']").attr('content') diff --git a/core/server/api/db.js b/core/server/api/db.js index dcb7b14264..e28423c0ee 100644 --- a/core/server/api/db.js +++ b/core/server/api/db.js @@ -10,7 +10,7 @@ var dataExport = require('../data/export'), _ = require('underscore'), schema = require('../data/schema'), config = require('../config'), - debugPath = config.paths().webroot + '/ghost/debug/', + debugPath = config.paths().subdir + '/ghost/debug/', db; @@ -142,7 +142,7 @@ db = { res.set({ "X-Cache-Invalidate": "/*" }); - res.redirect(config.paths().webroot + '/ghost/signin/'); + res.redirect(config.paths().subdir + '/ghost/signin/'); }); }).otherwise(function importFailure(error) { return apiNotifications.browse().then(function (notifications) { diff --git a/core/server/api/index.js b/core/server/api/index.js index f471147a2f..df261516db 100644 --- a/core/server/api/index.js +++ b/core/server/api/index.js @@ -57,7 +57,7 @@ requestHandler = function (apiMethod) { // If permalinks have changed, find old post route if (req.body.permalinks && req.body.permalinks !== permalinks) { for (i = 0; i < req.app.routes.get.length; i += 1) { - if (req.app.routes.get[i].path === config.paths().webroot + permalinks) { + if (req.app.routes.get[i].path === config.paths().subdir + permalinks) { postRouteIndex = i; break; } diff --git a/core/server/config/loader.js b/core/server/config/loader.js index b3ac64747f..d14943c0ed 100644 --- a/core/server/config/loader.js +++ b/core/server/config/loader.js @@ -49,7 +49,8 @@ function writeConfigFile() { } function validateConfigEnvironment() { - var envVal = process.env.NODE_ENV || 'undefined', + var envVal = process.env.NODE_ENV || undefined, + rejectMessage = 'Unable to load config', hasHostAndPort, hasSocket, config, @@ -65,20 +66,20 @@ function validateConfigEnvironment() { if (!config) { errors.logError(new Error('Cannot find the configuration for the current NODE_ENV'), "NODE_ENV=" + envVal, 'Ensure your config.js has a section for the current NODE_ENV value and is formatted properly.'); - return when.reject(); + return when.reject(rejectMessage); } // Check that our url is valid parsedUrl = url.parse(config.url || 'invalid', false, true); if (!parsedUrl.host) { errors.logError(new Error('Your site url in config.js is invalid.'), config.url, 'Please make sure this is a valid url before restarting'); - return when.reject(); + return when.reject(rejectMessage); } // Check that we have database values if (!config.database) { errors.logError(new Error('Your database configuration in config.js is invalid.'), JSON.stringify(config.database), 'Please make sure this is a valid Bookshelf database configuration'); - return when.reject(); + return when.reject(rejectMessage); } hasHostAndPort = config.server && !!config.server.host && !!config.server.port; @@ -87,7 +88,7 @@ function validateConfigEnvironment() { // Check for valid server host and port values if (!config.server || !(hasHostAndPort || hasSocket)) { errors.logError(new Error('Your server values (socket, or host and port) in config.js are invalid.'), JSON.stringify(config.server), 'Please provide them before restarting.'); - return when.reject(); + return when.reject(rejectMessage); } return when.resolve(config); diff --git a/core/server/config/paths.js b/core/server/config/paths.js index b525df1f15..7064318365 100644 --- a/core/server/config/paths.js +++ b/core/server/config/paths.js @@ -21,8 +21,7 @@ var path = require('path'), function paths() { return { 'appRoot': appRoot, - 'path': localPath, - 'webroot': localPath === '/' ? '' : localPath, + 'subdir': localPath === '/' ? '' : localPath, 'config': path.join(appRoot, 'config.js'), 'configExample': path.join(appRoot, 'config.example.js'), 'contentPath': contentPath, diff --git a/core/server/config/theme.js b/core/server/config/theme.js index d6630eb7fe..a58ab925b7 100644 --- a/core/server/config/theme.js +++ b/core/server/config/theme.js @@ -1,7 +1,7 @@ // Holds all theme configuration information // that as mostly used by templates and handlebar helpers. -var when = require('when'), +var when = require('when'), // Variables themeConfig = {}; @@ -23,8 +23,8 @@ function update(settings, configUrl) { settings.read('logo'), settings.read('cover') ]).then(function (globals) { - - themeConfig.url = configUrl; + // normalise the URL by removing any trailing slash + themeConfig.url = configUrl.replace(/\/$/, ''); themeConfig.title = globals[0].value; themeConfig.description = globals[1].value; themeConfig.logo = globals[2] ? globals[2].value : ''; diff --git a/core/server/controllers/admin.js b/core/server/controllers/admin.js index be20ead1c9..fa760f713b 100644 --- a/core/server/controllers/admin.js +++ b/core/server/controllers/admin.js @@ -86,7 +86,7 @@ adminControllers = { req.session.regenerate(function (err) { if (!err) { req.session.user = user.id; - var redirect = config.paths().webroot + '/ghost/'; + var redirect = config.paths().subdir + '/ghost/'; if (req.body.redirect) { redirect += decodeURIComponent(req.body.redirect); } @@ -137,7 +137,7 @@ adminControllers = { if (req.session.user === undefined) { req.session.user = user.id; } - res.json(200, {redirect: config.paths().webroot + '/ghost/'}); + res.json(200, {redirect: config.paths().subdir + '/ghost/'}); } }); }); @@ -179,7 +179,7 @@ adminControllers = { }; return api.notifications.add(notification).then(function () { - res.json(200, {redirect: config.paths().webroot + '/ghost/signin/'}); + res.json(200, {redirect: config.paths().subdir + '/ghost/signin/'}); }); }, function failure(error) { @@ -215,7 +215,7 @@ adminControllers = { errors.logError(err, 'admin.js', "Please check the provided token for validity and expiration."); return api.notifications.add(notification).then(function () { - res.redirect(config.paths().webroot + '/ghost/forgotten'); + res.redirect(config.paths().subdir + '/ghost/forgotten'); }); }); }, @@ -233,7 +233,7 @@ adminControllers = { }; return api.notifications.add(notification).then(function () { - res.json(200, {redirect: config.paths().webroot + '/ghost/signin/'}); + res.json(200, {redirect: config.paths().subdir + '/ghost/signin/'}); }); }).otherwise(function (err) { // TODO: Better error message if we can tell whether the passwords didn't match or something @@ -251,7 +251,7 @@ adminControllers = { }; return api.notifications.add(notification).then(function () { - res.redirect(config.paths().webroot + '/ghost/signin/'); + res.redirect(config.paths().subdir + '/ghost/signin/'); }); }, 'index': function (req, res) { diff --git a/core/server/controllers/frontend.js b/core/server/controllers/frontend.js index 6cc733f7b0..e6c7ba79ea 100644 --- a/core/server/controllers/frontend.js +++ b/core/server/controllers/frontend.js @@ -33,7 +33,7 @@ frontendControllers = { // Redirect '/page/1/' to '/' for all teh good SEO if (pageParam === 1 && req.route.path === '/page/:page/') { - return res.redirect(config.paths().webroot + '/'); + return res.redirect(config.paths().subdir + '/'); } // No negative posts per page, must be number @@ -54,7 +54,7 @@ frontendControllers = { // If page is greater than number of pages we have, redirect to last page if (pageParam > maxPage) { - return res.redirect(maxPage === 1 ? config.paths().webroot + '/' : (config.paths().webroot + '/page/' + maxPage + '/')); + return res.redirect(maxPage === 1 ? config.paths().subdir + '/' : (config.paths().subdir + '/page/' + maxPage + '/')); } // Render the page of posts @@ -116,11 +116,11 @@ frontendControllers = { // No negative pages if (isNaN(pageParam) || pageParam < 1) { - return res.redirect(config.paths().webroot + '/rss/'); + return res.redirect(config.paths().subdir + '/rss/'); } - if (pageParam === 1 && req.route.path === config.paths().webroot + '/rss/:page/') { - return res.redirect(config.paths().webroot + '/rss/'); + if (pageParam === 1 && req.route.path === config.paths().subdir + '/rss/:page/') { + return res.redirect(config.paths().subdir + '/rss/'); } api.posts.browse({page: pageParam}).then(function (page) { @@ -134,7 +134,7 @@ frontendControllers = { // If page is greater than number of pages we have, redirect to last page if (pageParam > maxPage) { - return res.redirect(config.paths().webroot + '/rss/' + maxPage + '/'); + return res.redirect(config.paths().subdir + '/rss/' + maxPage + '/'); } filters.doFilter('prePostsRender', page.posts).then(function (posts) { diff --git a/core/server/helpers/index.js b/core/server/helpers/index.js index e1bc07d91c..e563a59175 100644 --- a/core/server/helpers/index.js +++ b/core/server/helpers/index.js @@ -100,14 +100,12 @@ coreHelpers.url = function (options) { slug: function () { return self.slug; }, id: function () { return self.id; } }, - path = config.paths().path, isAbsolute = options && options.hash.absolute; return api.settings.read('permalinks').then(function (permalinks) { if (isAbsolute) { - output += config().url; - } - if (path && path !== '/') { - output += path; + output += config().url.replace(/\/$/, ''); + } else { + output += config.paths().subdir; } if (models.isPost(self)) { output += permalinks.value; @@ -131,14 +129,9 @@ coreHelpers.url = function (options) { // flag outputs the asset path for the Ghost admin coreHelpers.asset = function (context, options) { var output = '', - subDir = config.paths().path, isAdmin = options && options.hash && options.hash.ghost; - if (subDir === '/') { - output += '/'; - } else { - output += subDir + '/'; - } + output += config.paths().subdir + '/'; if (isAdmin) { output += 'ghost/'; @@ -269,8 +262,7 @@ coreHelpers.fileStorage = function (context, options) { }; coreHelpers.ghostScriptTags = function () { - var scriptFiles = [], - webroot = config.paths().webroot; + var scriptFiles = []; if (isProduction) { scriptFiles.push("ghost.min.js"); @@ -286,7 +278,7 @@ coreHelpers.ghostScriptTags = function () { scriptFiles = _.map(scriptFiles, function (fileName) { return scriptTemplate({ - source: webroot + '/built/scripts/' + fileName, + source: config.paths().subdir + '/built/scripts/' + fileName, version: version }); }); @@ -354,7 +346,6 @@ coreHelpers.post_class = function (options) { coreHelpers.ghost_head = function (options) { /*jslint unparam:true*/ var blog = config.theme(), - webroot = config.paths().webroot, head = [], majorMinor = /^(\d+\.)?(\d+)/, trimmedVersion = this.version; @@ -363,7 +354,9 @@ coreHelpers.ghost_head = function (options) { head.push(''); - head.push(''); + head.push(''); + if (this.ghostRoot) { head.push(''); } @@ -376,11 +369,10 @@ coreHelpers.ghost_head = function (options) { coreHelpers.ghost_foot = function (options) { /*jslint unparam:true*/ - var foot = [], - webroot = config.paths().webroot; + var foot = []; foot.push(scriptTemplate({ - source: (webroot === '/' ? '' : webroot) + '/shared/vendor/jquery/jquery.js', + source: config.paths().subdir + '/shared/vendor/jquery/jquery.js', version: this.version })); @@ -394,6 +386,7 @@ coreHelpers.meta_title = function (options) { /*jslint unparam:true*/ var title, blog; + if (_.isString(this.ghostRoot)) { if (!this.ghostRoot || this.ghostRoot === '/' || this.ghostRoot === '' || this.ghostRoot.match(/\/page/)) { blog = config.theme(); diff --git a/core/server/middleware/index.js b/core/server/middleware/index.js index 368b4a7413..823c57c230 100644 --- a/core/server/middleware/index.js +++ b/core/server/middleware/index.js @@ -31,7 +31,7 @@ function ghostLocals(req, res, next) { res.locals.version = packageInfo.version; res.locals.path = req.path; // Strip off the subdir part of the path - res.locals.ghostRoot = req.path.replace(config.paths().webroot, ''); + res.locals.ghostRoot = req.path.replace(config.paths().subdir, ''); if (res.isAdmin) { res.locals.csrfToken = req.csrfToken(); @@ -99,7 +99,7 @@ function activateTheme(activeTheme) { expressServer.enable(expressServer.get('activeTheme')); if (stackLocation) { expressServer.stack[stackLocation].handle = middleware.whenEnabled(expressServer.get('activeTheme'), middleware.staticTheme()); - expressServer.stack[stackLocation].route = config.paths().webroot; + expressServer.stack[stackLocation].route = config.paths().subdir; } // set view engine @@ -118,7 +118,7 @@ function activateTheme(activeTheme) { // Uses the URL to detect whether this response should be an admin response // This is used to ensure the right content is served, and is not for security purposes function manageAdminAndTheme(req, res, next) { - res.isAdmin = req.url.lastIndexOf(config.paths().webroot + '/ghost/', 0) === 0; + res.isAdmin = req.url.lastIndexOf(config.paths().subdir + '/ghost/', 0) === 0; if (res.isAdmin) { expressServer.enable('admin'); @@ -146,11 +146,10 @@ function manageAdminAndTheme(req, res, next) { // Redirect to signup if no users are currently created function redirectToSignup(req, res, next) { - var root = config.paths().webroot; /*jslint unparam:true*/ api.users.browse().then(function (users) { if (users.length === 0) { - return res.redirect(root + '/ghost/signup/'); + return res.redirect(config.paths().subdir + '/ghost/signup/'); } next(); }).otherwise(function (err) { @@ -190,7 +189,7 @@ function checkSSL(req, res, next) { module.exports = function (server, dbHash) { var oneHour = 60 * 60 * 1000, oneYear = 365 * 24 * oneHour, - root = config.paths().webroot, + subdir = config.paths().subdir, corePath = config.paths().corePath, cookie; @@ -206,15 +205,15 @@ module.exports = function (server, dbHash) { } // Favicon - expressServer.use(root, express.favicon(corePath + '/shared/favicon.ico')); + expressServer.use(subdir, express.favicon(corePath + '/shared/favicon.ico')); // Shared static config - expressServer.use(root + '/shared', express['static'](path.join(corePath, '/shared'))); + expressServer.use(subdir + '/shared', express['static'](path.join(corePath, '/shared'))); - expressServer.use(root + '/content/images', storage.get_storage().serve()); + expressServer.use(subdir + '/content/images', storage.get_storage().serve()); // Serve our built scripts; can't use /scripts here because themes already are - expressServer.use(root + '/built/scripts', express['static'](path.join(corePath, '/built/scripts'), { + expressServer.use(subdir + '/built/scripts', express['static'](path.join(corePath, '/built/scripts'), { // Put a maxAge of one year on built scripts maxAge: oneYear })); @@ -226,7 +225,7 @@ module.exports = function (server, dbHash) { expressServer.use(checkSSL); // Admin only config - expressServer.use(root + '/ghost', middleware.whenEnabled('admin', express['static'](path.join(corePath, '/client/assets')))); + expressServer.use(subdir + '/ghost', middleware.whenEnabled('admin', express['static'](path.join(corePath, '/client/assets')))); // Theme only config expressServer.use(middleware.whenEnabled(expressServer.get('activeTheme'), middleware.staticTheme())); @@ -237,12 +236,12 @@ module.exports = function (server, dbHash) { expressServer.use(express.json()); expressServer.use(express.urlencoded()); - expressServer.use(root + '/ghost/upload/', middleware.busboy); - expressServer.use(root + '/ghost/api/v0.1/db/', middleware.busboy); + expressServer.use(subdir + '/ghost/upload/', middleware.busboy); + expressServer.use(subdir + '/ghost/api/v0.1/db/', middleware.busboy); // Session handling cookie = { - path: root + '/ghost', + path: subdir + '/ghost', maxAge: 12 * oneHour }; @@ -271,7 +270,7 @@ module.exports = function (server, dbHash) { expressServer.use(initViews); // process the application routes - expressServer.use(root, expressServer.router); + expressServer.use(subdir, expressServer.router); // ### Error handling // 404 Handler diff --git a/core/server/middleware/middleware.js b/core/server/middleware/middleware.js index 39af2a2d97..69f5c0f007 100644 --- a/core/server/middleware/middleware.js +++ b/core/server/middleware/middleware.js @@ -45,7 +45,7 @@ var middleware = { } redirect = '?r=' + encodeURIComponent(reqPath); } - return res.redirect(config.paths().webroot + '/ghost/signin/' + redirect); + return res.redirect(config.paths().subdir + '/ghost/signin/' + redirect); }); } next(); @@ -67,7 +67,7 @@ var middleware = { // Login and signup forms in particular redirectToDashboard: function (req, res, next) { if (req.session.user) { - return res.redirect(config.paths().webroot + '/ghost/'); + return res.redirect(config.paths().subdir + '/ghost/'); } next(); diff --git a/core/server/routes/admin.js b/core/server/routes/admin.js index 54e5389ab1..58f6a6232a 100644 --- a/core/server/routes/admin.js +++ b/core/server/routes/admin.js @@ -5,28 +5,28 @@ var admin = require('../controllers/admin'), url = require('url'); module.exports = function (server) { - var root = config.paths().webroot; + var subdir = config.paths().subdir; // ### Admin routes /* TODO: put these somewhere in admin */ server.get('/logout/', function redirect(req, res) { /*jslint unparam:true*/ - res.redirect(301, root + '/ghost/signout/'); + res.redirect(301, subdir + '/ghost/signout/'); }); server.get('/signout/', function redirect(req, res) { /*jslint unparam:true*/ - res.redirect(301, root + '/ghost/signout/'); + res.redirect(301, subdir + '/ghost/signout/'); }); server.get('/signin/', function redirect(req, res) { /*jslint unparam:true*/ - res.redirect(301, root + '/ghost/signin/'); + res.redirect(301, subdir + '/ghost/signin/'); }); server.get('/signup/', function redirect(req, res) { /*jslint unparam:true*/ - res.redirect(301, root + '/ghost/signup/'); + res.redirect(301, subdir + '/ghost/signup/'); }); server.get('/ghost/login/', function redirect(req, res) { /*jslint unparam:true*/ - res.redirect(301, root + '/ghost/signin/'); + res.redirect(301, subdir + '/ghost/signin/'); }); server.get('/ghost/signout/', admin.logout); @@ -51,11 +51,11 @@ module.exports = function (server) { // redirect to /ghost and let that do the authentication to prevent redirects to /ghost//admin etc. server.get(/\/((ghost-admin|admin|wp-admin|dashboard|signin)\/?)$/, function (req, res) { /*jslint unparam:true*/ - res.redirect(root + '/ghost/'); + res.redirect(subdir + '/ghost/'); }); server.get(/\/(ghost$\/?)/, middleware.auth, function (req, res) { /*jslint unparam:true*/ - res.redirect(root + '/ghost/'); + res.redirect(subdir + '/ghost/'); }); server.get('/ghost/', middleware.redirectToSignup, middleware.auth, admin.index); }; \ No newline at end of file diff --git a/core/server/storage/localfilesystem.js b/core/server/storage/localfilesystem.js index 1103ad145a..d6efa1675f 100644 --- a/core/server/storage/localfilesystem.js +++ b/core/server/storage/localfilesystem.js @@ -33,7 +33,7 @@ localFileStore = _.extend(baseStore, { }).then(function () { // 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 - var fullUrl = (config.paths().webroot + '/' + targetFilename).replace(new RegExp('\\' + path.sep, 'g'), '/'); + var fullUrl = (config.paths().subdir + '/' + targetFilename).replace(new RegExp('\\' + path.sep, 'g'), '/'); return saved.resolve(fullUrl); }).otherwise(function (e) { errors.logError(e); diff --git a/core/test/unit/config_spec.js b/core/test/unit/config_spec.js index ef9c0b54f6..a0b321d091 100644 --- a/core/test/unit/config_spec.js +++ b/core/test/unit/config_spec.js @@ -3,6 +3,7 @@ var should = require('should'), sinon = require('sinon'), when = require('when'), + path = require('path'), config = require('../../server/config'); @@ -11,12 +12,13 @@ describe('Config', function () { describe('Theme', function () { var sandbox, + settings, settingsStub; beforeEach(function (done) { sandbox = sinon.sandbox.create(); - var settings = {'read': function read() {}}; + settings = {'read': function read() {}}; settingsStub = sandbox.stub(settings, 'read', function () { return when({value: 'casper'}); @@ -24,18 +26,26 @@ describe('Config', function () { config.theme.update(settings, 'http://my-ghost-blog.com') .then(done) - .otherwise(done); + .then(null, done); }); - afterEach(function () { + afterEach(function (done) { + config.theme.update(settings, config().url) + .then(done) + .then(null, done); + sandbox.restore(); }); - it('should have all of the values', function () { + it('should have exactly the right keys', function () { var themeConfig = config.theme(); // This will fail if there are any extra keys themeConfig.should.have.keys('url', 'title', 'description', 'logo', 'cover'); + }); + + it('should have the correct values for each key', function () { + var themeConfig = config.theme(); // Check values are as we expect themeConfig.should.have.property('url', 'http://my-ghost-blog.com'); @@ -46,8 +56,85 @@ describe('Config', function () { // Check settings.read gets called exactly 4 times settingsStub.callCount.should.equal(4); - }); }); + describe('Paths', function () { + var sandbox; + + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); + + afterEach(function (done) { + config.paths.update(config().url) + .then(done) + .then(null, done); + + sandbox.restore(); + }); + + it('should have exactly the right keys', function () { + var pathConfig = config.paths(); + + // This will fail if there are any extra keys + pathConfig.should.have.keys( + 'appRoot', + 'subdir', + 'config', + 'configExample', + 'contentPath', + 'corePath', + 'themePath', + 'pluginPath', + 'imagesPath', + 'imagesRelPath', + 'adminViews', + 'helperTemplates', + 'lang', + 'availableThemes', + 'availablePlugins' + ); + }); + + it('should have the correct values for each key', function () { + var pathConfig = config.paths(), + appRoot = path.resolve(__dirname, '../../../'); + + pathConfig.should.have.property('appRoot', appRoot); + pathConfig.should.have.property('subdir', ''); + }); + + it('should not return a slash for subdir', function (done) { + config.paths.update('http://my-ghost-blog.com').then(function () { + config.paths().should.have.property('subdir', ''); + + return config.paths.update('http://my-ghost-blog.com/'); + }).then(function () { + config.paths().should.have.property('subdir', ''); + + done(); + }).otherwise(done); + }); + + it('should handle subdirectories properly', function (done) { + config.paths.update('http://my-ghost-blog.com/blog').then(function () { + config.paths().should.have.property('subdir', '/blog'); + + return config.paths.update('http://my-ghost-blog.com/blog/'); + }).then(function () { + config.paths().should.have.property('subdir', '/blog'); + + return config.paths.update('http://my-ghost-blog.com/my/blog'); + }).then(function () { + config.paths().should.have.property('subdir', '/my/blog'); + + return config.paths.update('http://my-ghost-blog.com/my/blog/'); + }).then(function () { + config.paths().should.have.property('subdir', '/my/blog'); + + done(); + }).otherwise(done); + }); + }); }); \ No newline at end of file diff --git a/core/test/unit/helpers_template_spec.js b/core/test/unit/server_helpers_template_spec.js similarity index 100% rename from core/test/unit/helpers_template_spec.js rename to core/test/unit/server_helpers_template_spec.js diff --git a/core/test/unit/storage_localfilesystem_spec.js b/core/test/unit/storage_localfilesystem_spec.js index 27bdb7a014..c1a4601561 100644 --- a/core/test/unit/storage_localfilesystem_spec.js +++ b/core/test/unit/storage_localfilesystem_spec.js @@ -38,7 +38,7 @@ describe('Local File System Storage', function () { localfilesystem.save(image).then(function (url) { url.should.equal('/content/images/2013/Sep/IMAGE.jpg'); return done(); - }); + }).then(null, done); }); it('should send correct path to image when original file has spaces', function (done) { @@ -46,7 +46,7 @@ describe('Local File System Storage', function () { localfilesystem.save(image).then(function (url) { url.should.equal('/content/images/2013/Sep/AN_IMAGE.jpg'); return done(); - }); + }).then(null, done); }); it('should send correct path to image when date is in Jan 2014', function (done) { @@ -55,7 +55,7 @@ describe('Local File System Storage', function () { localfilesystem.save(image).then(function (url) { url.should.equal('/content/images/2014/Jan/IMAGE.jpg'); return done(); - }); + }).then(null, done); }); it('should create month and year directory', function (done) { @@ -97,7 +97,7 @@ describe('Local File System Storage', function () { localfilesystem.save(image).then(function (url) { url.should.equal('/content/images/2013/Sep/IMAGE-1.jpg'); return done(); - }); + }).then(null, done); }); it('can upload five different images with the same name without overwriting the first', function (done) { @@ -119,7 +119,7 @@ describe('Local File System Storage', function () { localfilesystem.save(image).then(function (url) { url.should.equal('/content/images/2013/Sep/IMAGE-4.jpg'); return done(); - }); + }).then(null, done); }); @@ -143,7 +143,7 @@ describe('Local File System Storage', function () { localfilesystem.save(image).then(function (url) { url.should.equal('/content/images/2013/Sep/IMAGE.jpg'); return done(); - }); + }).then(null, done); }); }); });