+
\ No newline at end of file
diff --git a/core/client/tpl/settings/sidebar.hbs b/core/client/tpl/settings/sidebar.hbs
index 246d0882c5..ad8295818b 100644
--- a/core/client/tpl/settings/sidebar.hbs
+++ b/core/client/tpl/settings/sidebar.hbs
@@ -5,5 +5,6 @@
\ No newline at end of file
diff --git a/core/client/views/settings.js b/core/client/views/settings.js
index 24bf32bfa8..9283924307 100644
--- a/core/client/views/settings.js
+++ b/core/client/views/settings.js
@@ -446,4 +446,70 @@
}
});
+ // ### Apps page
+ Settings.apps = Settings.Pane.extend({
+ id: "apps",
+
+ events: {
+ 'click .js-button-activate': 'activateApp',
+ 'click .js-button-deactivate': 'deactivateApp'
+ },
+
+ beforeRender: function () {
+ this.availableApps = this.model.toJSON().availableApps;
+ },
+
+ activateApp: function (event) {
+ var button = $(event.currentTarget);
+
+ button.removeClass('button-add').addClass('button js-button-active').text('Working');
+
+ this.saveStates();
+ },
+
+ deactivateApp: function (event) {
+ var button = $(event.currentTarget);
+
+ button.removeClass('button-delete js-button-active').addClass('button').text('Working');
+
+ this.saveStates();
+ },
+
+ saveStates: function () {
+ var activeButtons = this.$el.find('.js-apps .js-button-active'),
+ toSave = [],
+ self = this;
+
+ _.each(activeButtons, function (app) {
+ toSave.push($(app).data('app'));
+ });
+
+ this.model.save({
+ activeApps: JSON.stringify(toSave)
+ }, {
+ success: this.saveSuccess,
+ error: this.saveError
+ }).then(function () { self.render(); });
+ },
+
+ saveSuccess: function () {
+ Ghost.notifications.addItem({
+ type: 'success',
+ message: 'Active applications updated.',
+ status: 'passive',
+ id: 'success-1100'
+ });
+ },
+
+ saveError: function (xhr) {
+ Ghost.notifications.addItem({
+ type: 'error',
+ message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
+ status: 'passive'
+ });
+ },
+
+ templateName: 'settings/apps'
+ });
+
}());
diff --git a/core/server/api/settings.js b/core/server/api/settings.js
index c77313de59..f74d67d54f 100644
--- a/core/server/api/settings.js
+++ b/core/server/api/settings.js
@@ -9,6 +9,7 @@ var _ = require('lodash'),
settingsFilter,
updateSettingsCache,
readSettingsResult,
+ filterPaths,
// Holds cached settings
settingsCache = {};
@@ -78,36 +79,69 @@ readSettingsResult = function (result) {
}
})).then(function () {
return when(config().paths.availableThemes).then(function (themes) {
- var themeKeys = Object.keys(themes),
- res = [],
- i,
- item;
- for (i = 0; i < themeKeys.length; i += 1) {
- //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) {
- item.active = true;
- }
- res.push(item);
- }
- }
- settings.availableThemes = {};
- settings.availableThemes.value = res;
- settings.availableThemes.type = 'theme';
+ var res = filterPaths(themes, settings.activeTheme.value);
+ settings.availableThemes = {
+ value: res,
+ type: 'theme'
+ };
+ return settings;
+ });
+ }).then(function () {
+ return when(config().paths.availableApps).then(function (apps) {
+ var res = filterPaths(apps, JSON.parse(settings.activeApps.value));
+ settings.availableApps = {
+ value: res,
+ type: 'app'
+ };
return settings;
});
});
};
+/**
+ * Normalizes paths read by require-tree so that the apps and themes modules can use them.
+ * Creates an empty array (res), and populates it with useful info about the read packages
+ * like name, whether they're active (comparison with the second argument), and if they
+ * have a package.json, that, otherwise false
+ * @param object paths as returned by require-tree()
+ * @param array/string active as read from the settings object
+ * @return array of objects with useful info about
+ * apps / themes
+ */
+filterPaths = function (paths, active) {
+ var pathKeys = Object.keys(paths),
+ res = [],
+ item;
+
+ // turn active into an array (so themes and apps can be checked the same)
+ if (!Array.isArray(active)) {
+ active = [active];
+ }
+
+ _.each(pathKeys, function (key) {
+ //do not include hidden files or _messages
+ if (key.indexOf('.') !== 0
+ && key !== '_messages'
+ && key !== 'README.md'
+ ) {
+ item = {
+ name: key
+ };
+ if (paths[key].hasOwnProperty('package.json')) {
+ item.package = paths[key]['package.json'];
+ } else {
+ item.package = false;
+ }
+
+ if (_.indexOf(active, key) !== -1) {
+ item.active = true;
+ }
+ res.push(item);
+ }
+ });
+ return res;
+};
+
settings = {
// #### Browse
@@ -153,6 +187,7 @@ settings = {
var type = key.type;
delete key.type;
delete key.availableThemes;
+ delete key.availableApps;
key = settingsCollection(key);
return dataProvider.Settings.edit(key).then(function (result) {
diff --git a/core/server/controllers/admin.js b/core/server/controllers/admin.js
index e8f2d2f19c..4f85b2a61d 100644
--- a/core/server/controllers/admin.js
+++ b/core/server/controllers/admin.js
@@ -89,7 +89,7 @@ adminControllers = {
// Method: GET
'settings': function (req, res, next) {
// TODO: Centralise list/enumeration of settings panes, so we don't run into trouble in future.
- var allowedSections = ['', 'general', 'user'],
+ var allowedSections = ['', 'general', 'user', 'app'],
section = req.url.replace(/(^\/ghost\/settings[\/]*|\/$)/ig, '');
if (allowedSections.indexOf(section) < 0) {
diff --git a/core/test/functional/admin/settings_test.js b/core/test/functional/admin/settings_test.js
index cc4e50065c..1b3fb0e55a 100644
--- a/core/test/functional/admin/settings_test.js
+++ b/core/test/functional/admin/settings_test.js
@@ -1,6 +1,6 @@
/*globals casper, __utils__, url */
-CasperTest.begin("Settings screen is correct", 15, function suite(test) {
+CasperTest.begin("Settings screen is correct", 18, function suite(test) {
casper.thenOpen(url + "ghost/settings/", function testTitleAndUrl() {
test.assertTitle("Ghost Admin", "Ghost admin has no title");
test.assertUrlMatch(/ghost\/settings\/general\/$/, "Ghost doesn't require login this time");
@@ -10,6 +10,9 @@ CasperTest.begin("Settings screen is correct", 15, function suite(test) {
test.assertExists(".wrapper", "Settings main view is present");
test.assertExists(".settings-sidebar", "Settings sidebar view is present");
test.assertExists(".settings-menu", "Settings menu is present");
+ test.assertExists(".settings-menu .general", "General tab is present");
+ test.assertExists(".settings-menu .users", "Users tab is present");
+ test.assertExists(".settings-menu .apps", "Apps is present");
test.assertExists(".wrapper", "Settings main view is present");
test.assertExists(".settings-content", "Settings content view is present");
test.assertEval(function testGeneralIsActive() {
diff --git a/core/test/utils/api.js b/core/test/utils/api.js
index e55b3d30bf..92a21c3e89 100644
--- a/core/test/utils/api.js
+++ b/core/test/utils/api.js
@@ -12,7 +12,7 @@ var _ = require('lodash'),
// TODO: remove databaseVersion, dbHash
settings: ['databaseVersion', 'dbHash', 'title', 'description', 'email', 'logo', 'cover', 'defaultLang',
"permalinks", 'postsPerPage', 'forceI18n', 'activeTheme', 'activeApps', 'installedApps',
- 'availableThemes', 'nextUpdateCheck', 'displayUpdateNotification'],
+ 'availableThemes', 'availableApps', 'nextUpdateCheck', 'displayUpdateNotification'],
tag: ['id', 'uuid', 'name', 'slug', 'description', 'parent_id',
'meta_title', 'meta_description', 'created_at', 'created_by', 'updated_at', 'updated_by'],
user: ['id', 'uuid', 'name', 'slug', 'email', 'image', 'cover', 'bio', 'website',
From b4ea8bed61ef3d70a8806e877475580edb1dc42d Mon Sep 17 00:00:00 2001
From: Jacob Gable
Date: Fri, 28 Feb 2014 13:52:32 -0600
Subject: [PATCH 10/27] Refactor require-tree to not share messages
- Pass in messages to each method so they are not shared
- Export each method for calling individually
- Update reference to default export of require-tree
- Add default values for messages if not passed in
---
core/server/config/index.js | 2 +-
core/server/require-tree.js | 33 ++++++++++++++++++++++++++-------
2 files changed, 27 insertions(+), 8 deletions(-)
diff --git a/core/server/config/index.js b/core/server/config/index.js
index 1044891060..c63ec43163 100644
--- a/core/server/config/index.js
+++ b/core/server/config/index.js
@@ -7,7 +7,7 @@ var path = require('path'),
when = require('when'),
url = require('url'),
_ = require('lodash'),
- requireTree = require('../require-tree'),
+ requireTree = require('../require-tree').readAll,
theme = require('./theme'),
configUrl = require('./url'),
ghostConfig = {},
diff --git a/core/server/require-tree.js b/core/server/require-tree.js
index af957b06b5..339783f7a3 100644
--- a/core/server/require-tree.js
+++ b/core/server/require-tree.js
@@ -3,8 +3,13 @@ var _ = require('lodash'),
keys = require('when/keys'),
path = require('path'),
when = require('when'),
- messages = {errors: [], warns: []},
- parsePackageJson = function (path) {
+ parsePackageJson = function (path, messages) {
+ // Default the messages if non were passed
+ messages = messages || {
+ errors: [],
+ warns: []
+ };
+
var packageDeferred = when.defer(),
packagePromise = packageDeferred.promise,
jsonContainer;
@@ -30,8 +35,12 @@ var _ = require('lodash'),
});
return when(packagePromise);
},
- readDir = function (dir, options, depth) {
+ readDir = function (dir, options, depth, messages) {
depth = depth || 0;
+ messages = messages || {
+ errors: [],
+ warns: []
+ };
options = _.extend({
index: true
@@ -60,9 +69,9 @@ var _ = require('lodash'),
fs.lstat(fpath, function (error, result) {
/*jslint unparam:true*/
if (result.isDirectory()) {
- fileDeferred.resolve(readDir(fpath, options, depth + 1));
+ fileDeferred.resolve(readDir(fpath, options, depth + 1, messages));
} else if (depth === 1 && file === "package.json") {
- fileDeferred.resolve(parsePackageJson(fpath));
+ fileDeferred.resolve(parsePackageJson(fpath, messages));
} else {
fileDeferred.resolve(fpath);
}
@@ -79,7 +88,13 @@ var _ = require('lodash'),
});
},
readAll = function (dir, options, depth) {
- return when(readDir(dir, options, depth)).then(function (paths) {
+ // Start with clean messages, pass down along traversal
+ var messages = {
+ errors: [],
+ warns: []
+ };
+
+ return when(readDir(dir, options, depth, messages)).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) {
@@ -93,4 +108,8 @@ var _ = require('lodash'),
});
};
-module.exports = readAll;
+module.exports = {
+ readAll: readAll,
+ readDir: readDir,
+ parsePackageJson: parsePackageJson
+};
\ No newline at end of file
From 7155d95f9d1dcc78bb1a1a16decff782323b7416 Mon Sep 17 00:00:00 2001
From: Sebastian Gierlinger
Date: Wed, 26 Feb 2014 18:51:01 +0100
Subject: [PATCH 11/27] Add JSON API tests & cleanup
first 10 % of #2124
- added initial version of JSON API tests
- renamed error.errorCode to error.code
- renamed tags.all to tags.browse for consistency
---
Gruntfile.js | 5 +-
core/server/api/db.js | 6 +-
core/server/api/index.js | 2 +-
core/server/api/posts.js | 25 ++++----
core/server/api/settings.js | 6 +-
core/server/api/tags.js | 8 ++-
core/server/api/users.js | 10 +---
core/server/apps/proxy.js | 2 +-
core/server/controllers/frontend.js | 2 +-
core/server/filters.js | 2 +-
core/server/middleware/ghost-busboy.js | 2 +-
core/server/routes/api.js | 2 +-
.../integration/api/api_notifications_spec.js | 49 +++++++++++++++
core/test/integration/api/api_posts_spec.js | 59 +++++++++++++++++++
.../test/integration/api/api_settings_spec.js | 42 +++++++++++++
core/test/integration/api/api_tags_spec.js | 41 +++++++++++++
core/test/integration/api/api_users_spec.js | 41 +++++++++++++
.../integration/model/model_posts_spec.js | 12 ++--
core/test/utils/api.js | 3 +-
19 files changed, 276 insertions(+), 43 deletions(-)
create mode 100644 core/test/integration/api/api_notifications_spec.js
create mode 100644 core/test/integration/api/api_posts_spec.js
create mode 100644 core/test/integration/api/api_settings_spec.js
create mode 100644 core/test/integration/api/api_tags_spec.js
create mode 100644 core/test/integration/api/api_users_spec.js
diff --git a/Gruntfile.js b/Gruntfile.js
index d1c60be155..ed5777da3d 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -227,7 +227,10 @@ var path = require('path'),
},
integration: {
- src: ['core/test/integration/**/model*_spec.js']
+ src: [
+ 'core/test/integration/**/model*_spec.js',
+ 'core/test/integration/**/api*_spec.js'
+ ]
},
api: {
diff --git a/core/server/api/db.js b/core/server/api/db.js
index 3d1bb83d09..5db32833ca 100644
--- a/core/server/api/db.js
+++ b/core/server/api/db.js
@@ -34,7 +34,7 @@ db = {
* - If there is no path
* - If the name doesn't have json in it
*/
- return when.reject({errorCode: 500, message: 'Please select a .json file to import.'});
+ return when.reject({code: 500, message: 'Please select a .json file to import.'});
}
return api.settings.read({ key: 'databaseVersion' }).then(function (setting) {
@@ -99,7 +99,7 @@ db = {
}).then(function () {
return when.resolve({message: 'Posts, tags and other data successfully imported'});
}).otherwise(function importFailure(error) {
- return when.reject({errorCode: 500, message: error.message || error});
+ return when.reject({code: 500, message: error.message || error});
});
},
'deleteAllContent': function () {
@@ -107,7 +107,7 @@ db = {
.then(function () {
return when.resolve({message: 'Successfully deleted all content from your blog.'});
}, function (error) {
- return when.reject({errorCode: 500, message: error.message || error});
+ return when.reject({code: 500, message: error.message || error});
});
}
};
diff --git a/core/server/api/index.js b/core/server/api/index.js
index 67fc05b290..ccea747840 100644
--- a/core/server/api/index.js
+++ b/core/server/api/index.js
@@ -61,7 +61,7 @@ requestHandler = function (apiMethod) {
}
});
}, function (error) {
- var errorCode = error.errorCode || 500,
+ var errorCode = error.code || 500,
errorMsg = {error: _.isString(error) ? error : (_.isObject(error) ? error.message : 'Unknown API Error')};
res.json(errorCode, errorMsg);
});
diff --git a/core/server/api/posts.js b/core/server/api/posts.js
index b4be27853d..96ad75932c 100644
--- a/core/server/api/posts.js
+++ b/core/server/api/posts.js
@@ -1,8 +1,7 @@
var when = require('when'),
_ = require('lodash'),
dataProvider = require('../models'),
- permissions = require('../permissions'),
- canThis = permissions.canThis,
+ canThis = require('../permissions').canThis,
filteredUserAttributes = require('./users').filteredAttributes,
posts;
@@ -15,7 +14,7 @@ posts = {
options = options || {};
// **returns:** a promise for a page of posts in a json object
- //return dataProvider.Post.findPage(options);
+
return dataProvider.Post.findPage(options).then(function (result) {
var i = 0,
omitted = result;
@@ -43,7 +42,7 @@ posts = {
omitted.user = _.omit(omitted.user, filteredUserAttributes);
return omitted;
}
- return when.reject({errorCode: 404, message: 'Post not found'});
+ return when.reject({code: 404, message: 'Post not found'});
});
},
@@ -53,7 +52,7 @@ posts = {
if (slug) {
return slug;
}
- return when.reject({errorCode: 500, message: 'Could not generate slug'});
+ return when.reject({code: 500, message: 'Could not generate slug'});
});
},
@@ -63,7 +62,7 @@ posts = {
edit: function edit(postData) {
// **returns:** a promise for the resulting post in a json object
if (!this.user) {
- return when.reject({errorCode: 403, message: 'You do not have permission to edit this post.'});
+ return when.reject({code: 403, message: 'You do not have permission to edit this post.'});
}
var self = this;
return canThis(self.user).edit.post(postData.id).then(function () {
@@ -74,17 +73,17 @@ posts = {
omitted.user = _.omit(omitted.user, filteredUserAttributes);
return omitted;
}
- return when.reject({errorCode: 404, message: 'Post not found'});
+ return when.reject({code: 404, message: 'Post not found'});
}).otherwise(function (error) {
return dataProvider.Post.findOne({id: postData.id, status: 'all'}).then(function (result) {
if (!result) {
- return when.reject({errorCode: 404, message: 'Post not found'});
+ return when.reject({code: 404, message: 'Post not found'});
}
return when.reject({message: error.message});
});
});
}, function () {
- return when.reject({errorCode: 403, message: 'You do not have permission to edit this post.'});
+ return when.reject({code: 403, message: 'You do not have permission to edit this post.'});
});
},
@@ -94,13 +93,13 @@ posts = {
add: function add(postData) {
// **returns:** a promise for the resulting post in a json object
if (!this.user) {
- return when.reject({errorCode: 403, message: 'You do not have permission to add posts.'});
+ return when.reject({code: 403, message: 'You do not have permission to add posts.'});
}
return canThis(this.user).create.post().then(function () {
return dataProvider.Post.add(postData);
}, function () {
- return when.reject({errorCode: 403, message: 'You do not have permission to add posts.'});
+ return when.reject({code: 403, message: 'You do not have permission to add posts.'});
});
},
@@ -110,7 +109,7 @@ posts = {
destroy: function destroy(args) {
// **returns:** a promise for a json response with the id of the deleted post
if (!this.user) {
- return when.reject({errorCode: 403, message: 'You do not have permission to remove posts.'});
+ return when.reject({code: 403, message: 'You do not have permission to remove posts.'});
}
return canThis(this.user).remove.post(args.id).then(function () {
@@ -121,7 +120,7 @@ posts = {
});
});
}, function () {
- return when.reject({errorCode: 403, message: 'You do not have permission to remove posts.'});
+ return when.reject({code: 403, message: 'You do not have permission to remove posts.'});
});
}
};
diff --git a/core/server/api/settings.js b/core/server/api/settings.js
index f74d67d54f..ab8d4bb1ca 100644
--- a/core/server/api/settings.js
+++ b/core/server/api/settings.js
@@ -167,7 +167,7 @@ settings = {
if (settingsCache) {
return when(settingsCache[options.key]).then(function (setting) {
if (!setting) {
- return when.reject({errorCode: 404, message: 'Unable to find setting: ' + options.key});
+ return when.reject({code: 404, message: 'Unable to find setting: ' + options.key});
}
var res = {};
res.key = options.key;
@@ -202,7 +202,7 @@ settings = {
}).otherwise(function (error) {
return dataProvider.Settings.read(key.key).then(function (result) {
if (!result) {
- return when.reject({errorCode: 404, message: 'Unable to find setting: ' + key});
+ return when.reject({code: 404, message: 'Unable to find setting: ' + key});
}
return when.reject({message: error.message});
});
@@ -210,7 +210,7 @@ settings = {
}
return dataProvider.Settings.read(key).then(function (setting) {
if (!setting) {
- return when.reject({errorCode: 404, message: 'Unable to find setting: ' + key});
+ return when.reject({code: 404, message: 'Unable to find setting: ' + key});
}
if (!_.isString(value)) {
value = JSON.stringify(value);
diff --git a/core/server/api/tags.js b/core/server/api/tags.js
index ae0f0561b3..f73936585e 100644
--- a/core/server/api/tags.js
+++ b/core/server/api/tags.js
@@ -3,12 +3,14 @@ var dataProvider = require('../models'),
tags = {
- // #### All
+ // #### Browse
// **takes:** Nothing yet
- all: function browse() {
+ browse: function browse() {
// **returns:** a promise for all tags which have previously been used in a json object
- return dataProvider.Tag.findAll();
+ return dataProvider.Tag.findAll().then(function (result) {
+ return result.toJSON();
+ });
}
};
diff --git a/core/server/api/users.js b/core/server/api/users.js
index 1ed3c095da..d77a3071dd 100644
--- a/core/server/api/users.js
+++ b/core/server/api/users.js
@@ -8,8 +8,8 @@ var when = require('when'),
// ## Users
users = {
- // #### Browse
+ // #### Browse
// **takes:** options object
browse: function browse(options) {
// **returns:** a promise for a collection of users in a json object
@@ -31,7 +31,6 @@ users = {
},
// #### Read
-
// **takes:** an identifier (id or slug?)
read: function read(args) {
// **returns:** a promise for a single user in a json object
@@ -45,12 +44,11 @@ users = {
return omitted;
}
- return when.reject({errorCode: 404, message: 'User not found'});
+ return when.reject({code: 404, message: 'User not found'});
});
},
// #### Edit
-
// **takes:** a json object representing a user
edit: function edit(userData) {
// **returns:** a promise for the resulting user in a json object
@@ -60,12 +58,11 @@ users = {
var omitted = _.omit(result.toJSON(), filteredAttributes);
return omitted;
}
- return when.reject({errorCode: 404, message: 'User not found'});
+ return when.reject({code: 404, message: 'User not found'});
});
},
// #### Add
-
// **takes:** a json object representing a user
add: function add(userData) {
@@ -83,7 +80,6 @@ users = {
},
// #### Change Password
-
// **takes:** a json object representing a user
changePassword: function changePassword(userData) {
// **returns:** on success, returns a promise for the resulting user in a json object
diff --git a/core/server/apps/proxy.js b/core/server/apps/proxy.js
index 6689dbe7fe..fdf6e2e48b 100644
--- a/core/server/apps/proxy.js
+++ b/core/server/apps/proxy.js
@@ -15,7 +15,7 @@ var proxy = {
},
api: {
posts: _.pick(api.posts, 'browse', 'read'),
- tags: api.tags,
+ tags: _.pick(api.tags, 'browse'),
notifications: _.pick(api.notifications, 'add'),
settings: _.pick(api.settings, 'read')
}
diff --git a/core/server/controllers/frontend.js b/core/server/controllers/frontend.js
index 609d6e6f8c..90310596cd 100644
--- a/core/server/controllers/frontend.js
+++ b/core/server/controllers/frontend.js
@@ -59,7 +59,7 @@ function formatPageResponse(posts, page) {
function handleError(next) {
return function (err) {
var e = new Error(err.message);
- e.status = err.errorCode;
+ e.status = err.code;
return next(e);
};
}
diff --git a/core/server/filters.js b/core/server/filters.js
index 49e63ac0d4..393fdaf670 100644
--- a/core/server/filters.js
+++ b/core/server/filters.js
@@ -25,7 +25,7 @@ var Filters = function () {
// Register a new filter callback function
Filters.prototype.registerFilter = function (name, priority, fn) {
- // Curry the priority optional parameter to a default of 5
+ // Carry the priority optional parameter to a default of 5
if (_.isFunction(priority)) {
fn = priority;
priority = null;
diff --git a/core/server/middleware/ghost-busboy.js b/core/server/middleware/ghost-busboy.js
index 4531dec15f..a55d3be67c 100644
--- a/core/server/middleware/ghost-busboy.js
+++ b/core/server/middleware/ghost-busboy.js
@@ -55,7 +55,7 @@ function ghostBusBoy(req, res, next) {
busboy.on('limit', function () {
hasError = true;
- res.send(413, { errorCode: 413, message: 'File size limit breached.' });
+ res.send(413, {code: 413, message: 'File size limit breached.'});
});
busboy.on('error', function (error) {
diff --git a/core/server/routes/api.js b/core/server/routes/api.js
index b5b3cb8bb8..e14f97f824 100644
--- a/core/server/routes/api.js
+++ b/core/server/routes/api.js
@@ -19,7 +19,7 @@ module.exports = function (server) {
server.get('/ghost/api/v0.1/users/:id/', api.requestHandler(api.users.read));
server.put('/ghost/api/v0.1/users/:id/', api.requestHandler(api.users.edit));
// #### Tags
- server.get('/ghost/api/v0.1/tags/', api.requestHandler(api.tags.all));
+ server.get('/ghost/api/v0.1/tags/', api.requestHandler(api.tags.browse));
// #### Notifications
server.del('/ghost/api/v0.1/notifications/:id', api.requestHandler(api.notifications.destroy));
server.post('/ghost/api/v0.1/notifications/', api.requestHandler(api.notifications.add));
diff --git a/core/test/integration/api/api_notifications_spec.js b/core/test/integration/api/api_notifications_spec.js
new file mode 100644
index 0000000000..82155b48ae
--- /dev/null
+++ b/core/test/integration/api/api_notifications_spec.js
@@ -0,0 +1,49 @@
+/*globals describe, before, beforeEach, afterEach, it */
+var testUtils = require('../../utils'),
+ should = require('should'),
+
+ // Stuff we are testing
+ DataGenerator = require('../../utils/fixtures/data-generator'),
+ NotificationsAPI = require('../../../server/api/notifications');
+
+describe('Notifications API', function () {
+
+ before(function (done) {
+ testUtils.clearData().then(function () {
+ done();
+ }, done);
+ });
+
+ beforeEach(function (done) {
+ testUtils.initData()
+ .then(function () {
+ return testUtils.insertDefaultFixtures();
+ })
+ .then(function () {
+ done();
+ }, done);
+ });
+
+ afterEach(function (done) {
+ testUtils.clearData().then(function () {
+ done();
+ }, done);
+ });
+
+ it('can browse', function (done) {
+ var msg = {
+ type: 'error', // this can be 'error', 'success', 'warn' and 'info'
+ message: 'This is an error', // A string. Should fit in one line.
+ status: 'persistent', // or 'passive'
+ id: 'auniqueid' // A unique ID
+ };
+ NotificationsAPI.add(msg).then(function (notification){
+ NotificationsAPI.browse().then(function (results) {
+ should.exist(results);
+ results.length.should.be.above(0);
+ testUtils.API.checkResponse(results[0], 'notification');
+ done();
+ });
+ });
+ });
+});
\ No newline at end of file
diff --git a/core/test/integration/api/api_posts_spec.js b/core/test/integration/api/api_posts_spec.js
new file mode 100644
index 0000000000..107316dbdd
--- /dev/null
+++ b/core/test/integration/api/api_posts_spec.js
@@ -0,0 +1,59 @@
+/*globals describe, before, beforeEach, afterEach, it */
+var testUtils = require('../../utils'),
+ should = require('should'),
+
+ // Stuff we are testing
+ DataGenerator = require('../../utils/fixtures/data-generator'),
+ PostAPI = require('../../../server/api/posts');
+
+describe('Post API', function () {
+
+ before(function (done) {
+ testUtils.clearData().then(function () {
+ done();
+ }, done);
+ });
+
+ beforeEach(function (done) {
+ testUtils.initData()
+ .then(function () {
+ return testUtils.insertDefaultFixtures();
+ })
+ .then(function () {
+ done();
+ }, done);
+ });
+
+ afterEach(function (done) {
+ testUtils.clearData().then(function () {
+ done();
+ }, done);
+ });
+
+ it('can browse', function (done) {
+ PostAPI.browse().then(function (results) {
+ should.exist(results);
+ testUtils.API.checkResponse(results, 'posts');
+ should.exist(results.posts);
+ results.posts.length.should.be.above(0);
+ testUtils.API.checkResponse(results.posts[0], 'post');
+ done();
+ }).then(null, done);
+ });
+
+ it('can read', function (done) {
+ var firstPost;
+
+ PostAPI.browse().then(function (results) {
+ should.exist(results);
+ should.exist(results.posts);
+ results.posts.length.should.be.above(0);
+ firstPost = results.posts[0];
+ return PostAPI.read({slug: firstPost.slug});
+ }).then(function (found) {
+ should.exist(found);
+ testUtils.API.checkResponse(found, 'post');
+ done();
+ }).then(null, done);
+ });
+});
\ No newline at end of file
diff --git a/core/test/integration/api/api_settings_spec.js b/core/test/integration/api/api_settings_spec.js
new file mode 100644
index 0000000000..7e302f4c0f
--- /dev/null
+++ b/core/test/integration/api/api_settings_spec.js
@@ -0,0 +1,42 @@
+/*globals describe, before, beforeEach, afterEach, it */
+var testUtils = require('../../utils'),
+ should = require('should'),
+
+ // Stuff we are testing
+ DataGenerator = require('../../utils/fixtures/data-generator'),
+ SettingsAPI = require('../../../server/api/settings');
+
+describe('Settings API', function () {
+
+ before(function (done) {
+ testUtils.clearData().then(function () {
+ done();
+ }, done);
+ });
+
+ beforeEach(function (done) {
+ testUtils.initData()
+ .then(function () {
+ return testUtils.insertDefaultFixtures();
+ })
+ .then(function () {
+ done();
+ }, done);
+ });
+
+ afterEach(function (done) {
+ testUtils.clearData().then(function () {
+ done();
+ }, done);
+ });
+
+ it('can browse', function (done) {
+ SettingsAPI.updateSettingsCache().then(function () {
+ SettingsAPI.browse('blog').then(function (results) {
+ should.exist(results);
+ testUtils.API.checkResponse(results, 'settings');
+ done();
+ });
+ });
+ });
+});
\ No newline at end of file
diff --git a/core/test/integration/api/api_tags_spec.js b/core/test/integration/api/api_tags_spec.js
new file mode 100644
index 0000000000..dd34a975ac
--- /dev/null
+++ b/core/test/integration/api/api_tags_spec.js
@@ -0,0 +1,41 @@
+/*globals describe, before, beforeEach, afterEach, it */
+var testUtils = require('../../utils'),
+ should = require('should'),
+
+ // Stuff we are testing
+ DataGenerator = require('../../utils/fixtures/data-generator'),
+ TagsAPI = require('../../../server/api/tags');
+
+describe('Tags API', function () {
+
+ before(function (done) {
+ testUtils.clearData().then(function () {
+ done();
+ }, done);
+ });
+
+ beforeEach(function (done) {
+ testUtils.initData()
+ .then(function () {
+ return testUtils.insertDefaultFixtures();
+ })
+ .then(function () {
+ done();
+ }, done);
+ });
+
+ afterEach(function (done) {
+ testUtils.clearData().then(function () {
+ done();
+ }, done);
+ });
+
+ it('can browse', function (done) {
+ TagsAPI.browse().then(function (results) {
+ should.exist(results);
+ results.length.should.be.above(0);
+ testUtils.API.checkResponse(results[0], 'tag');
+ done();
+ }).then(null, done);
+ });
+});
\ No newline at end of file
diff --git a/core/test/integration/api/api_users_spec.js b/core/test/integration/api/api_users_spec.js
new file mode 100644
index 0000000000..9dfe467248
--- /dev/null
+++ b/core/test/integration/api/api_users_spec.js
@@ -0,0 +1,41 @@
+/*globals describe, before, beforeEach, afterEach, it */
+var testUtils = require('../../utils'),
+ should = require('should'),
+
+ // Stuff we are testing
+ DataGenerator = require('../../utils/fixtures/data-generator'),
+ UsersAPI = require('../../../server/api/users');
+
+describe('Users API', function () {
+
+ before(function (done) {
+ testUtils.clearData().then(function () {
+ done();
+ }, done);
+ });
+
+ beforeEach(function (done) {
+ testUtils.initData()
+ .then(function () {
+ return testUtils.insertDefaultFixtures();
+ })
+ .then(function () {
+ done();
+ }, done);
+ });
+
+ afterEach(function (done) {
+ testUtils.clearData().then(function () {
+ done();
+ }, done);
+ });
+
+ it('can browse', function (done) {
+ UsersAPI.browse().then(function (results) {
+ should.exist(results);
+ results.length.should.be.above(0);
+ testUtils.API.checkResponse(results[0], 'user');
+ done();
+ }).then(null, done);
+ });
+});
\ No newline at end of file
diff --git a/core/test/integration/model/model_posts_spec.js b/core/test/integration/model/model_posts_spec.js
index 98acee32fb..e702019150 100644
--- a/core/test/integration/model/model_posts_spec.js
+++ b/core/test/integration/model/model_posts_spec.js
@@ -1,13 +1,13 @@
/*globals describe, before, beforeEach, afterEach, it */
-var testUtils = require('../../utils'),
- should = require('should'),
- _ = require('lodash'),
- when = require('when'),
- sequence = require('when/sequence'),
+var testUtils = require('../../utils'),
+ should = require('should'),
+ _ = require('lodash'),
+ when = require('when'),
+ sequence = require('when/sequence'),
// Stuff we are testing
DataGenerator = require('../../utils/fixtures/data-generator'),
- Models = require('../../../server/models');
+ Models = require('../../../server/models');
describe('Post Model', function () {
diff --git a/core/test/utils/api.js b/core/test/utils/api.js
index 92a21c3e89..2284ba4a73 100644
--- a/core/test/utils/api.js
+++ b/core/test/utils/api.js
@@ -17,7 +17,8 @@ var _ = require('lodash'),
'meta_title', 'meta_description', 'created_at', 'created_by', 'updated_at', 'updated_by'],
user: ['id', 'uuid', 'name', 'slug', 'email', 'image', 'cover', 'bio', 'website',
'location', 'accessibility', 'status', 'language', 'meta_title', 'meta_description',
- 'created_at', 'updated_at']
+ 'created_at', 'updated_at'],
+ notification: ['type', 'message', 'status', 'id']
};
From b461cc6138be4e771f8b8954919bbfcbce1303e0 Mon Sep 17 00:00:00 2001
From: Gabor Javorszky
Date: Sat, 1 Mar 2014 08:57:14 +0000
Subject: [PATCH 12/27] Update link to clean up history in contributing.md
Fixes #2296
Also replaces line endings, not sure why (tried with all the settings,
git would just not have it)
---
CONTRIBUTING.md | 104 ++++++++++++++++++++++++------------------------
1 file changed, 52 insertions(+), 52 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 090838ac73..d3162267aa 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,11 +1,11 @@
# Contributing to Ghost
-So you're interested in giving us a hand? That's awesome! We've put together some brief guidelines that should help
+So you're interested in giving us a hand? That's awesome! We've put together some brief guidelines that should help
you get started quickly and easily.
There are lots and lots of ways to get involved, this document covers:
-* [raising issues](#raising-issues)
+* [raising issues](#raising-issues)
* [bug reports](#bugs)
* [feature requests](#features)
* [change requests](#changes)
@@ -19,14 +19,14 @@ There are lots and lots of ways to get involved, this document covers:
## Reporting An Issue
-If you're about to raise an issue because think you've found a problem with Ghost, or you'd like to make a request
+If you're about to raise an issue because think you've found a problem with Ghost, or you'd like to make a request
for a new feature in the codebase, or any other reason… please read this first.
The GitHub issue tracker is the preferred channel for [bug reports](#bugs),
[feature requests](#features), [change requests](#changes) and [submitting pull
requests](#pull-requests), but please respect the following restrictions:
-* Please **search for existing issues**. Help us keep duplicate issues to a minimum by checking to see if someone
+* Please **search for existing issues**. Help us keep duplicate issues to a minimum by checking to see if someone
has already reported your problem or requested your idea.
* Please **do not** use the issue tracker for personal support requests (use
@@ -51,13 +51,13 @@ Guidelines for bug reports:
3. **Isolate the problem** — ideally create a [reduced test
case](http://css-tricks.com/6263-reduced-test-cases/) and a live example.
-4. **Include a screencast if relevant** - Is your issue about a design or front end feature or bug? The most
-helpful thing in the world is if we can *see* what you're talking about.
+4. **Include a screencast if relevant** - Is your issue about a design or front end feature or bug? The most
+helpful thing in the world is if we can *see* what you're talking about.
Use [LICEcap](http://www.cockos.com/licecap/) to quickly and easily record a short screencast (24fps) and save it as an animated gif! Embed it directly into your GitHub issue. Kapow.
5. Use the Bug Report template below or [click this link](https://github.com/TryGhost/Ghost/issues/new?title=Bug%3A&body=%23%23%23%20Issue%20Summary%0A%0A%23%23%23%20Steps%20to%20Reproduce%0A%0A1.%20This%20is%20the%20first%20step%0A%0AThis%20is%20a%20bug%20because...%0A%0A%23%23%23%20Technical%20details%0A%0A*%20Ghost%20Version%3A%20master%20-%20latest%20commit%3A%20%20INSERT%20COMMIT%20REF%0A*%20Client%20OS%3A%20%0A*%20Server%20OS%3A%20%0A*%20Node%20Version%3A%20%0A*%20Browser%3A) to start creating a bug report with the template automatically.
-A good bug report shouldn't leave others needing to chase you up for more information. Be sure to include the
+A good bug report shouldn't leave others needing to chase you up for more information. Be sure to include the
details of your environment.
Here is a [real example](https://github.com/TryGhost/Ghost/issues/413)
@@ -80,7 +80,7 @@ suitable, include the steps required to reproduce the bug.
Any other information you want to share that is relevant to the issue being
reported. Especially, why do you consider this to be a bug? What do you expect to happen instead?
-### Technical details:
+### Technical details:
* Ghost Version: master (latest commit: 590ba48988b51b9c5e8d99afbb84c997436d7f21)
* Client OS: Mac OS X 10.8.4
@@ -95,38 +95,38 @@ reported. Especially, why do you consider this to be a bug? What do you expect t
Feature requests are welcome. Before you submit one be sure to have:
1. Read the [Roadmap](https://github.com/TryGhost/Ghost/wiki/Roadmap) and
-[Planned Features](https://github.com/TryGhost/Ghost/wiki/Planned-Features) listing, **use the GitHub search** and
+[Planned Features](https://github.com/TryGhost/Ghost/wiki/Planned-Features) listing, **use the GitHub search** and
check the feature hasn't already been requested.
-2. Take a moment to think about whether your idea fits with the scope and aims of the project, or if it might
+2. Take a moment to think about whether your idea fits with the scope and aims of the project, or if it might
better fit being an app/plugin.
-3. Remember, it's up to *you* to make a strong case to convince the project's leaders of the merits of this
-feature. Please provide as much detail and context as possible, this means explaining the use case and why it is
-likely to be common.
+3. Remember, it's up to *you* to make a strong case to convince the project's leaders of the merits of this
+feature. Please provide as much detail and context as possible, this means explaining the use case and why it is
+likely to be common.
4. Clearly indicate whether this is a feature request for Ghost admin, or for themes or apps.
### Change Requests
-Change requests cover both architectural and functional changes to how Ghost works. If you have an idea for a
+Change requests cover both architectural and functional changes to how Ghost works. If you have an idea for a
new or different dependency, a refactor, or an improvement to a feature, etc - please be sure to:
1. **Use the GitHub search** and check someone else didn't get there first
-2. Take a moment to think about the best way to make a case for, and explain what you're thinking. Are you sure
+2. Take a moment to think about the best way to make a case for, and explain what you're thinking. Are you sure
this shouldn't really be a [bug report](#bug-reports) or a [feature request](#feature-requests)? Is it really one
idea or is it many? What's the context? What problem are you solving? Why is what you are suggesting better than
-what's already there? Does it fit with the Roadmap?
+what's already there? Does it fit with the Roadmap?
### Submitting Pull Requests
-Pull requests are awesome. If you're looking to raise a PR for something which doesn't have an open issue, please think carefully about [raising an issue](#raising-issues) which your PR can close, especially if you're fixing a bug. This makes it more likely that there will be enough information available for your PR to be properly tested and merged. To make sure your PR is accepted as quickly as possible, you should be sure to have read
+Pull requests are awesome. If you're looking to raise a PR for something which doesn't have an open issue, please think carefully about [raising an issue](#raising-issues) which your PR can close, especially if you're fixing a bug. This makes it more likely that there will be enough information available for your PR to be properly tested and merged. To make sure your PR is accepted as quickly as possible, you should be sure to have read
all the guidelines on:
* [code standards](https://github.com/TryGhost/Ghost/wiki/Code-standards)
* [commit messages](https://github.com/TryGhost/Ghost/wiki/Git-workflow#commit-messages)
-* [cleaning-up history](https://github.com/TryGhost/Ghost/wiki/Git-workflow#clean-up-history)
+* [cleaning-up history](https://github.com/TryGhost/Ghost/wiki/Git-workflow#wiki-clean-up-history)
* [not breaking the build](https://github.com/TryGhost/Ghost/wiki/Git-workflow#check-it-passes-the-tests)
##### Need Help?
@@ -138,29 +138,29 @@ If you're not completely clear on how to submit / update / *do* Pull Requests, p
### Testing and Quality Assurance
-Never underestimate just how useful quality assurance is. If you're looking to get involved with the code base and
+Never underestimate just how useful quality assurance is. If you're looking to get involved with the code base and
don't know where to start, checking out and testing a pull request is one of the most useful things you could do.
-If you want to get involved with testing Ghost, there is a set of
+If you want to get involved with testing Ghost, there is a set of
[QA Documentation](https://github.com/TryGhost/Ghost/wiki/QA-Documentation) on the wiki.
-Essentially though, [check out the latest master](#core), take it for a spin, and if you find anything odd, please
+Essentially though, [check out the latest master](#core), take it for a spin, and if you find anything odd, please
follow the [bug report guidelines](#bug-reports) and let us know!
#### Checking out a Pull Request
-These are some [excellent instructions](https://gist.github.com/piscisaureus/3342247) on configuring your GitHub
-repository to allow you to checkout pull requests in the same way as branches:
+These are some [excellent instructions](https://gist.github.com/piscisaureus/3342247) on configuring your GitHub
+repository to allow you to checkout pull requests in the same way as branches:
.
### Documentation
-Ghost's main documentation can be found at [docs.ghost.org](http://docs.ghost.org).
+Ghost's main documentation can be found at [docs.ghost.org](http://docs.ghost.org).
-The documentation is generated using jekyll, all of the docs are on the gh-pages branch on the GitHub repository.
-You can clone the repo, checkout the gh-pages branch, and submit pull requests following
+The documentation is generated using jekyll, all of the docs are on the gh-pages branch on the GitHub repository.
+You can clone the repo, checkout the gh-pages branch, and submit pull requests following
the [pull-request](#pull-requests) guidelines.
@@ -174,7 +174,7 @@ Full documentation on contributing translations can be found at
## Working on Ghost Core
-**Note:** It is recommended that you use the [Ghost-Vagrant](https://github.com/TryGhost/Ghost-Vagrant) setup for
+**Note:** It is recommended that you use the [Ghost-Vagrant](https://github.com/TryGhost/Ghost-Vagrant) setup for
developing Ghost.
**Pre-requisites:**
@@ -215,25 +215,25 @@ Addresses for development:
### Updating with the latest changes
-Pulling down the latest changes from master will often require more than just a pull, you may also need to do one
+Pulling down the latest changes from master will often require more than just a pull, you may also need to do one
or more of the following:
* `npm install` - fetch any new dependencies
* `git submodule update` - fetch the latest changes to Casper (the default theme)
- * `grunt` - will recompile handlebars templates and sass for the admin (as long as you have previously
+ * `grunt` - will recompile handlebars templates and sass for the admin (as long as you have previously
run `grunt init` to install bourbon)
* delete content/data/*.db - delete the database and allow Ghost to recreate the fixtures
### Key Branches & Tags
-- **[master](https://github.com/TryGhost/Ghost)** is the bleeding edge development branch. All work on the next
+- **[master](https://github.com/TryGhost/Ghost)** is the bleeding edge development branch. All work on the next
release is here.
- **[gh-pages](http://tryghost.github.io/Ghost)** is The Ghost Guide documentation for Getting Started with Ghost.
### Compiling CSS & JavaScript
-A SASS compiler is required to work with the CSS in this project. You can either do this by running `grunt` from
-the command line - or by using a 3rd party app. We recommend [CodeKit](http://incident57.com/codekit/) (Paid/Mac)
+A SASS compiler is required to work with the CSS in this project. You can either do this by running `grunt` from
+the command line - or by using a 3rd party app. We recommend [CodeKit](http://incident57.com/codekit/) (Paid/Mac)
& [Scout](http://mhs.github.io/scout-app/) (Free/Mac/PC).
You will need to have Ruby installed, as well as having run `gem install sass && gem install bourbon`.
@@ -241,19 +241,19 @@ You will need to have Ruby installed, as well as having run `gem install sass &&
Ghost uses Grunt heavily to automate useful tasks such as building assets, testing, live reloading/watching etc etc
-[Grunt Toolkit docs](https://github.com/TryGhost/Ghost/wiki/Grunt-Toolkit) are a worthwhile read for any would-be
+[Grunt Toolkit docs](https://github.com/TryGhost/Ghost/wiki/Grunt-Toolkit) are a worthwhile read for any would-be
contributor.
## Troubleshooting / FAQ
### I get "ERROR: Failed to lookup view "index"
-Sounds like you don't have our default theme - Casper, your content/themes/casper folder is probably empty.
-When cloning from GitHub be sure to use SSH and to run `git submodule update --init`.
+Sounds like you don't have our default theme - Casper, your content/themes/casper folder is probably empty.
+When cloning from GitHub be sure to use SSH and to run `git submodule update --init`.
### I get "Syntax error: File to import not found or unreadable: bourbon/_bourbon."
-Sounds like you don't have the Ruby gem "bourbon" installed. Make sure you have Ruby, and then
+Sounds like you don't have the Ruby gem "bourbon" installed. Make sure you have Ruby, and then
run `gem install bourbon`, and `grunt init`.
### Ghost doesn't do anything - I get a blank screen
@@ -262,31 +262,31 @@ Sounds like you probably didn't run the right grunt command for building assets
### SQLite3 doesn't install properly during npm install
-Ghost depends upon SQLite3, which requires a native binary. These are provided for most major platforms, but if you
-are using a more obscure *nix flavor you may need to follow
+Ghost depends upon SQLite3, which requires a native binary. These are provided for most major platforms, but if you
+are using a more obscure *nix flavor you may need to follow
the [node-sqlite3 binary instructions](https://github.com/developmentseed/node-sqlite3/wiki/Binaries).
## Contributor License Agreement
-By contributing your code to Ghost you grant the Ghost Foundation a non-exclusive, irrevocable, worldwide,
-royalty-free, sublicenseable, transferable license under all of Your relevant intellectual property rights
-(including copyright, patent, and any other rights), to use, copy, prepare derivative works of, distribute and
-publicly perform and display the Contributions on any licensing terms, including without limitation:
-(a) open source licenses like the MIT license; and (b) binary, proprietary, or commercial licenses. Except for the
+By contributing your code to Ghost you grant the Ghost Foundation a non-exclusive, irrevocable, worldwide,
+royalty-free, sublicenseable, transferable license under all of Your relevant intellectual property rights
+(including copyright, patent, and any other rights), to use, copy, prepare derivative works of, distribute and
+publicly perform and display the Contributions on any licensing terms, including without limitation:
+(a) open source licenses like the MIT license; and (b) binary, proprietary, or commercial licenses. Except for the
licenses granted herein, You reserve all right, title, and interest in and to the Contribution.
-You confirm that you are able to grant us these rights. You represent that You are legally entitled to grant the
-above license. If Your employer has rights to intellectual property that You create, You represent that You have
-received permission to make the Contributions on behalf of that employer, or that Your employer has waived such
+You confirm that you are able to grant us these rights. You represent that You are legally entitled to grant the
+above license. If Your employer has rights to intellectual property that You create, You represent that You have
+received permission to make the Contributions on behalf of that employer, or that Your employer has waived such
rights for the Contributions.
-You represent that the Contributions are Your original works of authorship, and to Your knowledge, no other person
-claims, or has the right to claim, any right in any invention or patent related to the Contributions. You also
-represent that You are not legally obligated, whether by entering into an agreement or otherwise, in any way that
+You represent that the Contributions are Your original works of authorship, and to Your knowledge, no other person
+claims, or has the right to claim, any right in any invention or patent related to the Contributions. You also
+represent that You are not legally obligated, whether by entering into an agreement or otherwise, in any way that
conflicts with the terms of this license.
-The Ghost Foundation acknowledges that, except as explicitly described in this Agreement, any Contribution which
-you provide is on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED,
-INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS
+The Ghost Foundation acknowledges that, except as explicitly described in this Agreement, any Contribution which
+you provide is on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED,
+INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS
FOR A PARTICULAR PURPOSE.
From 561ea0edbb4860381c683efefd16a39adc1687a9 Mon Sep 17 00:00:00 2001
From: Sean Hellwig
Date: Fri, 28 Feb 2014 23:25:49 -0800
Subject: [PATCH 13/27] Add plugin icons to Apps menu item in Ghost settings
closes #2290
- added css entry in settings.scss for to display plugin icon for apps menu item
- remove unused css entry for .plugins in settings.scss
---
core/client/assets/sass/layouts/settings.scss | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/core/client/assets/sass/layouts/settings.scss b/core/client/assets/sass/layouts/settings.scss
index 30f235d51a..ba429b5a4f 100644
--- a/core/client/assets/sass/layouts/settings.scss
+++ b/core/client/assets/sass/layouts/settings.scss
@@ -162,7 +162,7 @@
.services a { @include icon($i-services) }
.users a { @include icon($i-users) }
.appearance a { @include icon($i-appearance) }
- .plugins a { @include icon($i-plugins) }
+ .apps a { @include icon($i-plugins) }
}//.settings-menu
From be8b9cf0922886b747285fb284ea42710673ef75 Mon Sep 17 00:00:00 2001
From: Johan Stenehall
Date: Sun, 2 Mar 2014 12:46:03 +0100
Subject: [PATCH 14/27] Fixing typo in allowedSections for allowed pages under
settings
---
core/server/controllers/admin.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/core/server/controllers/admin.js b/core/server/controllers/admin.js
index e0fd494446..0ed5c14a98 100644
--- a/core/server/controllers/admin.js
+++ b/core/server/controllers/admin.js
@@ -89,7 +89,7 @@ adminControllers = {
// Method: GET
'settings': function (req, res, next) {
// TODO: Centralise list/enumeration of settings panes, so we don't run into trouble in future.
- var allowedSections = ['', 'general', 'user', 'app'],
+ var allowedSections = ['', 'general', 'user', 'apps'],
section = req.url.replace(/(^\/ghost\/settings[\/]*|\/$)/ig, '');
if (allowedSections.indexOf(section) < 0) {
From ab2656960a9ed7c93b58857d519b7c1d78961ea7 Mon Sep 17 00:00:00 2001
From: Shashank Mehta
Date: Mon, 3 Mar 2014 02:30:09 +0530
Subject: [PATCH 15/27] Prevent settings page from rendering same page twice
Closes #2316
- There was a check to prevent rerendering of same content pane but it wasn't working
- Fixed the check for this
---
core/client/views/settings.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/core/client/views/settings.js b/core/client/views/settings.js
index 9283924307..bc40977f99 100644
--- a/core/client/views/settings.js
+++ b/core/client/views/settings.js
@@ -61,7 +61,7 @@
Ghost.router.navigate('/settings/' + id + '/');
Ghost.trigger('urlchange');
- if (this.pane && id === this.pane.el.id) {
+ if (this.pane && id === this.pane.id) {
return;
}
_.result(this.pane, 'destroy');
From a92c8085c56a16937514c090fa6db47b5b2c616b Mon Sep 17 00:00:00 2001
From: Shashank Mehta
Date: Sat, 1 Mar 2014 03:34:05 +0530
Subject: [PATCH 16/27] Shifts app UI behind config option
Closes #2287
- adds helper for checking whether to show apps UI or not
- hides app UI from settings page
---
core/client/views/settings.js | 5 +++++
core/server/helpers/index.js | 16 ++++++++++++++++
core/server/views/settings.hbs | 2 +-
3 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/core/client/views/settings.js b/core/client/views/settings.js
index 9283924307..c19ad1d631 100644
--- a/core/client/views/settings.js
+++ b/core/client/views/settings.js
@@ -38,6 +38,11 @@
initialize: function (options) {
this.render();
this.menu = this.$('.settings-menu');
+ // Hides apps UI unless config.js says otherwise
+ // This will stay until apps UI is ready to ship
+ if ($(this.el).attr('data-apps') !== "true") {
+ this.menu.find('.apps').hide();
+ }
this.showContent(options.pane);
},
diff --git a/core/server/helpers/index.js b/core/server/helpers/index.js
index 9c4deb8cdf..02a66202f2 100644
--- a/core/server/helpers/index.js
+++ b/core/server/helpers/index.js
@@ -327,6 +327,20 @@ coreHelpers.file_storage = function (context, options) {
return "true";
};
+// ### Apps helper
+//
+// *Usage example:*
+// `{{apps}}`
+//
+// Returns the config value for apps.
+coreHelpers.apps = function (context, options) {
+ /*jslint unparam:true*/
+ if (config().hasOwnProperty('apps')) {
+ return config().apps.toString();
+ }
+ return "false";
+};
+
coreHelpers.ghost_script_tags = function () {
var scriptList = isProduction ? scriptFiles.production : scriptFiles.development;
@@ -785,6 +799,8 @@ registerHelpers = function (adminHbs, assetHash) {
registerAdminHelper('file_storage', coreHelpers.file_storage);
+ registerAdminHelper('apps', coreHelpers.apps);
+
registerAdminHelper('admin_url', coreHelpers.admin_url);
registerAsyncAdminHelper('update_notification', coreHelpers.update_notification);
diff --git a/core/server/views/settings.hbs b/core/server/views/settings.hbs
index 9ddbc5b628..253476f313 100644
--- a/core/server/views/settings.hbs
+++ b/core/server/views/settings.hbs
@@ -1,6 +1,6 @@
{{!< default}}