diff --git a/app.js b/app.js index 9db9c6a720..bbc0da3830 100755 --- a/app.js +++ b/app.js @@ -45,13 +45,7 @@ ghost.app().use(ghost.initTheme(ghost.app())); ghost.app().use(flash()); // bind locals - options which appear in every view - perhaps this should be admin only - ghost.app().use(function (req, res, next) { - res.locals.messages = req.flash(); - res.locals.siteTitle = ghostGlobals.title; - res.locals.siteDescription = ghostGlobals.description; - res.locals.siteUrl = ghostGlobals.url; - next(); - }); + }); /** @@ -88,6 +82,8 @@ // Make sure we have a locals value. res.locals = res.locals || {}; + + // Extend it with nav data and ghostGlobals _.extend(res.locals, navData, { ghostGlobals: ghost.globals() @@ -101,10 +97,22 @@ ghost.loaded = loading.promise; when.all([ghost.init(), filters.loadCoreFilters(ghost), helpers.loadCoreHelpers(ghost)]).then(function () { - // Assign the globals we have loaded ghostGlobals = ghost.globals(); + ghost.app().use(function (req, res, next) { + res.locals.messages = req.flash(); + res.locals.siteTitle = ghostGlobals.title; + res.locals.siteDescription = ghostGlobals.description; + res.locals.siteUrl = ghostGlobals.url; + res.locals.activeTheme = ghostGlobals.activeTheme; + res.locals.activePlugin = ghostGlobals.activePlugin; + res.locals.availableThemes = ghost.paths().availableThemes; + res.locals.availablePlugins = ghost.paths().availablePlugins; + + next(); + }); + /** * API routes.. * @todo auth should be public auth not user auth diff --git a/core/admin/views/settings.hbs b/core/admin/views/settings.hbs index 8ecdbf52b3..ce36e6f0be 100644 --- a/core/admin/views/settings.hbs +++ b/core/admin/views/settings.hbs @@ -13,7 +13,7 @@
  • General
  • Content
  • Users
  • -
  • Appearance
  • +
  • Appearance
  • Connected Services
  • Plugins
  • @@ -200,4 +200,17 @@ +
    +
    +

    Appearance

    +
    +
    +
    Raw json be here
    +

    Active theme: {{json settings.activeTheme}}

    +

    Available themes: {{json availableThemes}}

    +

    Available plugins: {{json availablePlugins}}

    + + +
    +
    \ No newline at end of file diff --git a/core/frontend/helpers/index.js b/core/frontend/helpers/index.js index 51c2e5e1da..8b2860b218 100644 --- a/core/frontend/helpers/index.js +++ b/core/frontend/helpers/index.js @@ -40,6 +40,10 @@ return output; }); + ghost.registerThemeHelper('json', function (object, options) { + return JSON.stringify(object); + }); + return when.all([ // Just one async helper for now, but could be more in the future navHelper.registerWithGhost(ghost) diff --git a/core/ghost.js b/core/ghost.js index dacb950f53..72aa965eed 100644 --- a/core/ghost.js +++ b/core/ghost.js @@ -16,6 +16,11 @@ models = require('./shared/models'), ExampleFilter = require('../content/plugins/exampleFilters'), + + requireTree = require('./shared/require-tree'), + themeDirectories = requireTree('content/themes'), + pluginDirectories = requireTree('content/plugins'), + Ghost, instance, defaults, @@ -59,10 +64,17 @@ instance = this; // Holds the filters - this.filterCallbacks = []; + instance.filterCallbacks = []; // Holds the filter hooks (that are built in to Ghost Core) - this.filters = []; + instance.filters = []; + + // Holds the theme directories temporarily + instance.themeDirectories = {}; + + // Holds the plugin directories temporarily + instance.pluginDirectories = {}; + plugin = new ExampleFilter(instance).init(); @@ -74,32 +86,44 @@ // load Plugins... // var f = new FancyFirstChar(ghost).init(); + + _.extend(instance, { app: function () { return app; }, config: function () { return config; }, - globals: function () { return instance.globalConfig; }, // there's no management here to be sure this has loaded + + // there's no management here to be sure this has loaded + globals: function () { return instance.globalConfig; }, dataProvider: models, statuses: function () { return statuses; }, polyglot: function () { return polyglot; }, plugin: function () { return plugin; }, + getPaths: function () { + return when.all([themeDirectories, pluginDirectories]).then(function (paths) { + instance.themeDirectories = paths[0]; + instance.pluginDirectories = paths[1]; + return; + }); + }, paths: function () { return { 'activeTheme': __dirname + '/../content/' + config.themeDir + '/' + config.activeTheme + '/', 'adminViews': __dirname + '/admin/views/', 'frontendViews': __dirname + '/frontend/views/', - 'lang': __dirname + '/lang/' + 'lang': __dirname + '/lang/', + 'availableThemes': instance.themeDirectories, + 'availablePlugins': instance.pluginDirectories }; } - }); - } return instance; }; Ghost.prototype.init = function () { this.globalConfig = config.blogData; - return when.all([instance.dataProvider.init()]); + + return when.all([instance.dataProvider.init(), instance.getPaths()]); }; /** @@ -186,7 +210,6 @@ var self = this; return function initTheme(req, res, next) { app.set('view engine', 'hbs'); - if (/(^\/ghost$|^\/ghost\/)/.test(req.url) === false) { app.engine('hbs', hbs.express3( {partialsDir: self.paths().activeTheme + 'partials'} @@ -199,7 +222,6 @@ } app.use(express['static'](self.paths().activeTheme)); app.use('/content/images', express['static'](path.join(__dirname, '/../content/images'))); - next(); }; }; diff --git a/core/shared/data/fixtures/001.js b/core/shared/data/fixtures/001.js index 5144091f72..cdee1c76fb 100644 --- a/core/shared/data/fixtures/001.js +++ b/core/shared/data/fixtures/001.js @@ -60,6 +60,20 @@ module.exports = { "created_by": 1, "updated_by": 1, "type": "general" + }, + { + "key": "activePlugins", + "value": "", + "created_by": 1, + "updated_by": 1, + "type": "general" + }, + { + "key": "activeTheme", + "value": "content/themes/casper", + "created_by": 1, + "updated_by": 1, + "type": "general" } ], diff --git a/core/shared/require-tree.js b/core/shared/require-tree.js new file mode 100644 index 0000000000..5a403a18e5 --- /dev/null +++ b/core/shared/require-tree.js @@ -0,0 +1,64 @@ +(function() { + "use strict"; + var when = require('when'), + keys = require('when/keys'), + fs = require('fs'), + path = require('path'), + extend = function (obj, source) { + var key; + for (key in source) { + if (source.hasOwnProperty(key)) { + obj[key] = source[key]; + } + } + return obj; + }, + readDir = function (dir, options, depth) { + depth = depth || 0; + + options = extend({ + index: true + }, options); + + if (depth > 1) { + return null; + } + + var subtree = {}, + treeDeferred = when.defer(), + treePromise = treeDeferred.promise; + + fs.readdir(dir, function (error, files) { + 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; + fs.lstat(fpath, function (error, result) { + if (result.isDirectory()) { + fileDeferred.resolve(readDir(fpath, options, depth + 1)); + } else { + fileDeferred.resolve(fpath); + } + }); + }); + + return keys.all(subtree).then(function(theFiles) { + return treeDeferred.resolve(theFiles); + }); + }); + + return when(treePromise).then(function (prom) { + return prom; + }); + }, + readAll = function (dir, options, depth) { + return when(readDir(dir, options, depth)).then(function (paths) { + return paths; + }); + }; + + module.exports = readAll; +}()); \ No newline at end of file