diff --git a/core/client/tpl/settings/general.hbs b/core/client/tpl/settings/general.hbs index 55842a45d2..c2c91f313f 100644 --- a/core/client/tpl/settings/general.hbs +++ b/core/client/tpl/settings/general.hbs @@ -69,7 +69,8 @@

Select a theme for your blog

diff --git a/core/server/api/settings.js b/core/server/api/settings.js index bfff1981f1..c77313de59 100644 --- a/core/server/api/settings.js +++ b/core/server/api/settings.js @@ -83,10 +83,15 @@ readSettingsResult = function (result) { i, item; for (i = 0; i < themeKeys.length; i += 1) { - //do not include hidden files - if (themeKeys[i].indexOf('.') !== 0) { + //do not include hidden files or _messages + if (themeKeys[i].indexOf('.') !== 0 && themeKeys[i] !== '_messages') { item = {}; item.name = themeKeys[i]; + if (themes[themeKeys[i]].hasOwnProperty('package.json')) { + item.package = themes[themeKeys[i]]['package.json']; + } else { + item.package = false; + } //data about files currently not used //item.details = themes[themeKeys[i]]; if (themeKeys[i] === settings.activeTheme.value) { diff --git a/core/server/controllers/frontend.js b/core/server/controllers/frontend.js index 1c4b20c0e9..0d4eca5138 100644 --- a/core/server/controllers/frontend.js +++ b/core/server/controllers/frontend.js @@ -118,7 +118,7 @@ frontendControllers = { filters.doFilter('prePostsRender', post).then(function (post) { api.settings.read('activeTheme').then(function (activeTheme) { var paths = config().paths.availableThemes[activeTheme.value], - view = post.page && paths.hasOwnProperty('page') ? 'page' : 'post'; + view = post.page && paths.hasOwnProperty('page.hbs') ? 'page' : 'post'; res.render(view, {post: post}); }); }); diff --git a/core/server/index.js b/core/server/index.js index 1fc3e6021a..ab99aa82cd 100644 --- a/core/server/index.js +++ b/core/server/index.js @@ -71,7 +71,7 @@ function initDbHashAndFirstRun() { } // Checks for the existence of the "built" javascript files from grunt concat. -// Returns a promise that will be resolved if all files exist or rejected if +// Returns a promise that will be resolved if all files exist or rejected if // any are missing. function builtFilesExist() { var deferreds = [], @@ -262,7 +262,12 @@ function setup(server) { startGhost ); } - + _.each(config().paths.availableThemes._messages.errors, function (error) { + errors.logError(error.message, error.context); + }); + _.each(config().paths.availableThemes._messages.warns, function (warn) { + errors.logWarn(warn.message, warn.context); + }); }); }, function (err) { errors.logErrorAndExit(err, err.context, err.help); diff --git a/core/server/require-tree.js b/core/server/require-tree.js index 87619df3d0..7c76110705 100644 --- a/core/server/require-tree.js +++ b/core/server/require-tree.js @@ -1,8 +1,10 @@ -var when = require('when'), - keys = require('when/keys'), - fs = require('fs'), - path = require('path'), - extend = function (obj, source) { +var when = require('when'), + keys = require('when/keys'), + fs = require('fs'), + path = require('path'), + _ = require('lodash'), + messages = {errors: [], warns: []}, + extend = function (obj, source) { var key; for (key in source) { if (source.hasOwnProperty(key)) { @@ -11,6 +13,32 @@ var when = require('when'), } return obj; }, + parsePackageJson = function (path) { + var packageDeferred = when.defer(), + packagePromise = packageDeferred.promise, + jsonContainer; + + fs.readFile(path, function (error, data) { + if (error) { + messages.errors.push({message: 'Could not read package.json file', context: path}); + packageDeferred.resolve(false); + return; + } + try { + jsonContainer = JSON.parse(data); + if (jsonContainer.hasOwnProperty('name') && jsonContainer.hasOwnProperty('version')) { + packageDeferred.resolve(jsonContainer); + } else { + messages.errors.push({message: '"name" or "version" is missing from theme package.json file.', context: path}); + packageDeferred.resolve(false); + } + } catch (e) { + messages.errors.push({message: 'Theme package.json file is malformed', context: path}); + packageDeferred.resolve(false); + } + }); + return when(packagePromise); + }, readDir = function (dir, options, depth) { depth = depth || 0; @@ -36,14 +64,14 @@ var when = require('when'), files.forEach(function (file) { var fileDeferred = when.defer(), filePromise = fileDeferred.promise, - ext = path.extname(file), - name = path.basename(file, ext), fpath = path.join(dir, file); - subtree[name] = filePromise; + subtree[file] = filePromise; fs.lstat(fpath, function (error, result) { /*jslint unparam:true*/ if (result.isDirectory()) { fileDeferred.resolve(readDir(fpath, options, depth + 1)); + } else if (depth === 1 && file === "package.json") { + fileDeferred.resolve(parsePackageJson(fpath)); } else { fileDeferred.resolve(fpath); } @@ -61,6 +89,15 @@ var when = require('when'), }, readAll = function (dir, options, depth) { return when(readDir(dir, options, depth)).then(function (paths) { + // for all contents of the dir, I'm interested in the ones that are directories and within /theme/ + if (typeof paths === "object" && dir.indexOf('theme') !== -1) { + _.each(paths, function (path, index) { + if (typeof path === 'object' && !path.hasOwnProperty('package.json') && index.indexOf('.') !== 0) { + messages.warns.push({message: 'Theme does not have a package.json file', context: index}); + } + }); + } + paths._messages = messages; return paths; }); }; diff --git a/core/test/unit/frontend_spec.js b/core/test/unit/frontend_spec.js index 6c6fb9ec32..6d1a59113d 100644 --- a/core/test/unit/frontend_spec.js +++ b/core/test/unit/frontend_spec.js @@ -195,10 +195,10 @@ describe('Frontend Controller', function () { 'availableThemes': { 'casper': { 'assets': null, - 'default': '/content/themes/casper/default.hbs', - 'index': '/content/themes/casper/index.hbs', - 'page': '/content/themes/casper/page.hbs', - 'post': '/content/themes/casper/post.hbs' + 'default.hbs': '/content/themes/casper/default.hbs', + 'index.hbs': '/content/themes/casper/index.hbs', + 'page.hbs': '/content/themes/casper/page.hbs', + 'post.hbs': '/content/themes/casper/post.hbs' } } }