issue #58 - removing the iiwf

Function wrapper and use strict pragma removed from all node files
This commit is contained in:
Hannah Wolfe 2013-06-25 12:43:15 +01:00
parent 2c7245e0e5
commit ba810fb0bb
39 changed files with 3645 additions and 3840 deletions

View File

@ -1,307 +1,302 @@
(function () {
"use strict";
var path = require('path'),
buildDirectory = path.resolve(process.cwd(), '../build'),
distDirectory = path.resolve(process.cwd(), '../dist'),
configureGrunt = function (grunt) {
var path = require('path'),
buildDirectory = path.resolve(process.cwd(), '../build'),
distDirectory = path.resolve(process.cwd(), '../dist'),
configureGrunt = function (grunt) {
var cfg = {
// Common paths to be used by tasks
paths: {
adminAssets: './core/admin/assets',
build: buildDirectory,
nightlyBuild: path.join(buildDirectory, 'nightly'),
buildBuild: path.join(buildDirectory, 'build'),
dist: distDirectory,
nightlyDist: path.join(distDirectory, 'nightly'),
buildDist: path.join(distDirectory, 'build')
},
var cfg = {
// Common paths to be used by tasks
paths: {
adminAssets: './core/admin/assets',
build: buildDirectory,
nightlyBuild: path.join(buildDirectory, 'nightly'),
buildBuild: path.join(buildDirectory, 'build'),
dist: distDirectory,
nightlyDist: path.join(distDirectory, 'nightly'),
buildDist: path.join(distDirectory, 'build')
},
pkg: grunt.file.readJSON('package.json'),
pkg: grunt.file.readJSON('package.json'),
// JSLint all the things!
jslintm: {
node: {
directives: {
// node environment
node: true,
// browser environment
browser: false,
// allow dangling underscores in var names
nomen: true,
// allow to do statements
todo: true,
// allow unused parameters
unparam: true,
// don't require use strict pragma
sloppy: true
},
files: {
src: [
"*.js",
"core/**/*.js"
]
},
// Lint core files, but not libs, frontend or hbs files
exclude: [
"**/assets/lib/**/*.js",
"**/assets/js/**/*.js",
"**/assets/tpl/*.js"
// JSLint all the things!
jslintm: {
node: {
directives: {
// node environment
node: true,
// browser environment
browser: false,
// allow dangling underscores in var names
nomen: true,
// allow to do statements
todo: true,
// allow unused parameters
unparam: true,
// don't require use strict pragma
sloppy: true
},
files: {
src: [
"*.js",
"core/**/*.js"
]
},
frontend: {
directives: {
// node environment
node: false,
// browser environment
browser: true,
// allow dangling underscores in var names
nomen: true,
// allow to do statements
todo: true,
// allow unused parameters
unparam: true
},
files: {
src: "**/assets/js/**/*.js"
}
}
// Lint core files, but not libs, frontend or hbs files
exclude: [
"**/assets/lib/**/*.js",
"**/assets/js/**/*.js",
"**/assets/tpl/*.js"
]
},
mochaTest: {
options: {
ui: "bdd",
reporter: "spec"
frontend: {
directives: {
// node environment
node: false,
// browser environment
browser: true,
// allow dangling underscores in var names
nomen: true,
// allow to do statements
todo: true,
// allow unused parameters
unparam: true
},
all: {
src: ['core/test/**/*_spec.js']
},
api: {
src: ['core/test/**/api*_spec.js']
},
perm: {
src: ['core/test/**/permissions_spec.js']
}
},
// Compile all the SASS!
sass: {
admin: {
files: {
'<%= paths.adminAssets %>/css/screen.css': '<%= paths.adminAssets %>/sass/screen.scss'
}
}
},
shell: {
bourbon: {
command: 'bourbon install --path <%= paths.adminAssets %>/sass/modules/'
},
commitNightly: {
command: 'git commit package.json -m "Nightly build <%= pkg.version %>"'
},
tagNightly: {
command: 'git tag <%= pkg.version %> -a -m "Nightly build <%= pkg.version %>"'
},
pushMaster: {
command: 'git push origin master'
},
pushMasterTags: {
command: 'git push origin master --tags'
}
},
handlebars: {
core: {
options: {
namespace: "JST",
processName: function (filename) {
filename = filename.replace('./core/admin/assets/tpl/', '');
return filename.replace('.hbs', '');
}
},
files: {
"<%= paths.adminAssets %>/tpl/hbs-tpl.js": "<%= paths.adminAssets %>/tpl/**/*.hbs"
}
}
},
groc: {
application: [
"README.md",
"config.js",
"app.js",
"core/ghost.js",
"core/admin/assets/js/*.js",
"core/admin/assets/js/models/*.js",
"core/admin/assets/js/views/*.js",
"core/admin/controllers/*.js",
"core/frontend/controllers/*.js",
"core/frontend/filters/*.js",
"core/frontend/helpers/*.js",
"core/lang/i18n.js",
"core/shared/models/*.js",
"core/shared/permissions/*.js",
"core/shared/*.js",
"core/test/ghost/*.js",
"core/test/ghost.js"
],
options: {
"out": "./docs/"
}
},
watch: {
handlebars: {
files: '<%= paths.adminAssets %>/tpl/**/*.hbs',
tasks: ['handlebars']
},
sass: {
files: '<%= paths.adminAssets %>/sass/**/*',
tasks: ['sass:admin']
}
},
copy: {
nightly: {
files: [{
expand: true,
src: [
'**',
'!node_modules/**',
'!core/shared/data/*.db',
'!.sass*',
'!.af*',
'!.git*',
'!.groc*',
'!.travis.yml'
],
dest: "<%= paths.nightlyBuild %>/<%= pkg.version %>/"
}]
},
build: {
files: [{
expand: true,
src: [
'**',
'!node_modules/**',
'!core/shared/data/*.db',
'!.sass*',
'!.af*',
'!.git*',
'!.groc*',
'!.travis.yml'
],
dest: "<%= paths.buildBuild %>/"
}]
}
},
compress: {
nightly: {
options: {
archive: "<%= paths.nightlyDist %>/Ghost-Nightly-<%= pkg.version %>.zip"
},
expand: true,
cwd: "<%= paths.nightlyBuild %>/<%= pkg.version %>/",
src: ["**"]
},
build: {
options: {
archive: "<%= paths.buildDist %>/Ghost-Build.zip"
},
expand: true,
cwd: "<%= paths.buildBuild %>/",
src: ["**"]
files: {
src: "**/assets/js/**/*.js"
}
}
};
},
grunt.initConfig(cfg);
mochaTest: {
options: {
ui: "bdd",
reporter: "spec"
},
grunt.loadNpmTasks("grunt-jslint");
grunt.loadNpmTasks("grunt-mocha-test");
grunt.loadNpmTasks("grunt-shell");
grunt.loadNpmTasks("grunt-bump");
all: {
src: ['core/test/**/*_spec.js']
},
grunt.loadNpmTasks("grunt-contrib-compress");
grunt.loadNpmTasks("grunt-contrib-copy");
grunt.loadNpmTasks("grunt-contrib-watch");
grunt.loadNpmTasks("grunt-contrib-sass");
grunt.loadNpmTasks("grunt-contrib-handlebars");
grunt.loadNpmTasks('grunt-groc');
api: {
src: ['core/test/**/api*_spec.js']
},
// Update the package information after changes
grunt.registerTask('updateCurrentPackageInfo', function () {
cfg.pkg = grunt.file.readJSON('package.json');
});
perm: {
src: ['core/test/**/permissions_spec.js']
}
},
// jslintm aliased to jslint
grunt.registerTask("jslint", ["jslintm"]);
// Compile all the SASS!
sass: {
admin: {
files: {
'<%= paths.adminAssets %>/css/screen.css': '<%= paths.adminAssets %>/sass/screen.scss'
}
}
},
// Prepare the project for development
// TODO: Git submodule init/update (https://github.com/jaubourg/grunt-update-submodules)?
grunt.registerTask("init", ["shell:bourbon", "sass:admin", 'handlebars']);
shell: {
bourbon: {
command: 'bourbon install --path <%= paths.adminAssets %>/sass/modules/'
},
// Run API tests only
grunt.registerTask("test-api", ["mochaTest:api"]);
commitNightly: {
command: 'git commit package.json -m "Nightly build <%= pkg.version %>"'
},
// Run permisisons tests only
grunt.registerTask("test-p", ["mochaTest:perm"]);
tagNightly: {
command: 'git tag <%= pkg.version %> -a -m "Nightly build <%= pkg.version %>"'
},
// Run tests and lint code
grunt.registerTask("validate", ["jslint", "mochaTest:all"]);
pushMaster: {
command: 'git push origin master'
},
// Generate Docs
grunt.registerTask("docs", ["groc"]);
pushMasterTags: {
command: 'git push origin master --tags'
}
},
/* Nightly builds
* - Bump patch version in package.json,
* - Copy files to build-folder/nightly/#{version} directory
* - Clean out unnecessary files (travis, .git*, .af*, .groc*)
* - Zip files in build folder to dist-folder/#{version} directory
* - git commit package.json -m "Nightly build #{version}"
* - git tag -a -m "Nightly build #{version}"
* - git push origin master
* - git push origin master --tags
* - TODO: POST to pubsubhubub to notify of new build?
*/
grunt.registerTask("nightly", [
"shell:bourbon",
"sass:admin",
"handlebars",
"validate",
"bump",
"updateCurrentPackageInfo",
"copy:nightly",
"compress:nightly"
/* Caution: shit gets real below here */
//"shell:commitNightly",
//"shell:tagNightly",
//"shell:pushMaster",
//"shell:pushMasterTags"
]);
handlebars: {
core: {
options: {
namespace: "JST",
processName: function (filename) {
filename = filename.replace('./core/admin/assets/tpl/', '');
return filename.replace('.hbs', '');
}
},
files: {
"<%= paths.adminAssets %>/tpl/hbs-tpl.js": "<%= paths.adminAssets %>/tpl/**/*.hbs"
}
}
},
grunt.registerTask("build", [
"shell:bourbon",
"sass:admin",
"handlebars",
"copy:build",
"compress:build"
]);
groc: {
application: [
"README.md",
"config.js",
"app.js",
"core/ghost.js",
"core/admin/assets/js/*.js",
"core/admin/assets/js/models/*.js",
"core/admin/assets/js/views/*.js",
"core/admin/controllers/*.js",
"core/frontend/controllers/*.js",
"core/frontend/filters/*.js",
"core/frontend/helpers/*.js",
"core/lang/i18n.js",
"core/shared/models/*.js",
"core/shared/permissions/*.js",
"core/shared/*.js",
"core/test/ghost/*.js",
"core/test/ghost.js"
],
options: {
"out": "./docs/"
}
},
// When you just say "grunt"
grunt.registerTask("default", ['sass:admin', 'handlebars']);
watch: {
handlebars: {
files: '<%= paths.adminAssets %>/tpl/**/*.hbs',
tasks: ['handlebars']
},
sass: {
files: '<%= paths.adminAssets %>/sass/**/*',
tasks: ['sass:admin']
}
},
copy: {
nightly: {
files: [{
expand: true,
src: [
'**',
'!node_modules/**',
'!core/shared/data/*.db',
'!.sass*',
'!.af*',
'!.git*',
'!.groc*',
'!.travis.yml'
],
dest: "<%= paths.nightlyBuild %>/<%= pkg.version %>/"
}]
},
build: {
files: [{
expand: true,
src: [
'**',
'!node_modules/**',
'!core/shared/data/*.db',
'!.sass*',
'!.af*',
'!.git*',
'!.groc*',
'!.travis.yml'
],
dest: "<%= paths.buildBuild %>/"
}]
}
},
compress: {
nightly: {
options: {
archive: "<%= paths.nightlyDist %>/Ghost-Nightly-<%= pkg.version %>.zip"
},
expand: true,
cwd: "<%= paths.nightlyBuild %>/<%= pkg.version %>/",
src: ["**"]
},
build: {
options: {
archive: "<%= paths.buildDist %>/Ghost-Build.zip"
},
expand: true,
cwd: "<%= paths.buildBuild %>/",
src: ["**"]
}
}
};
module.exports = configureGrunt;
grunt.initConfig(cfg);
}());
grunt.loadNpmTasks("grunt-jslint");
grunt.loadNpmTasks("grunt-mocha-test");
grunt.loadNpmTasks("grunt-shell");
grunt.loadNpmTasks("grunt-bump");
grunt.loadNpmTasks("grunt-contrib-compress");
grunt.loadNpmTasks("grunt-contrib-copy");
grunt.loadNpmTasks("grunt-contrib-watch");
grunt.loadNpmTasks("grunt-contrib-sass");
grunt.loadNpmTasks("grunt-contrib-handlebars");
grunt.loadNpmTasks('grunt-groc');
// Update the package information after changes
grunt.registerTask('updateCurrentPackageInfo', function () {
cfg.pkg = grunt.file.readJSON('package.json');
});
// jslintm aliased to jslint
grunt.registerTask("jslint", ["jslintm"]);
// Prepare the project for development
// TODO: Git submodule init/update (https://github.com/jaubourg/grunt-update-submodules)?
grunt.registerTask("init", ["shell:bourbon", "sass:admin", 'handlebars']);
// Run API tests only
grunt.registerTask("test-api", ["mochaTest:api"]);
// Run permisisons tests only
grunt.registerTask("test-p", ["mochaTest:perm"]);
// Run tests and lint code
grunt.registerTask("validate", ["jslint", "mochaTest:all"]);
// Generate Docs
grunt.registerTask("docs", ["groc"]);
/* Nightly builds
* - Bump patch version in package.json,
* - Copy files to build-folder/nightly/#{version} directory
* - Clean out unnecessary files (travis, .git*, .af*, .groc*)
* - Zip files in build folder to dist-folder/#{version} directory
* - git commit package.json -m "Nightly build #{version}"
* - git tag -a -m "Nightly build #{version}"
* - git push origin master
* - git push origin master --tags
* - TODO: POST to pubsubhubub to notify of new build?
*/
grunt.registerTask("nightly", [
"shell:bourbon",
"sass:admin",
"handlebars",
"validate",
"bump",
"updateCurrentPackageInfo",
"copy:nightly",
"compress:nightly"
/* Caution: shit gets real below here */
//"shell:commitNightly",
//"shell:tagNightly",
//"shell:pushMaster",
//"shell:pushMasterTags"
]);
grunt.registerTask("build", [
"shell:bourbon",
"sass:admin",
"handlebars",
"copy:build",
"compress:build"
]);
// When you just say "grunt"
grunt.registerTask("default", ['sass:admin', 'handlebars']);
};
module.exports = configureGrunt;

297
app.js
View File

@ -1,172 +1,167 @@
// # Ghost main app file
/*global require, __dirname */
(function () {
"use strict";
// Module dependencies.
var express = require('express'),
when = require('when'),
_ = require('underscore'),
errors = require('./core/shared/errorHandling'),
admin = require('./core/admin/controllers'),
frontend = require('./core/frontend/controllers'),
api = require('./core/shared/api'),
flash = require('connect-flash'),
Ghost = require('./core/ghost'),
I18n = require('./core/lang/i18n'),
filters = require('./core/frontend/filters'),
helpers = require('./core/frontend/helpers'),
// Module dependencies.
var express = require('express'),
when = require('when'),
_ = require('underscore'),
errors = require('./core/shared/errorHandling'),
admin = require('./core/admin/controllers'),
frontend = require('./core/frontend/controllers'),
api = require('./core/shared/api'),
flash = require('connect-flash'),
Ghost = require('./core/ghost'),
I18n = require('./core/lang/i18n'),
filters = require('./core/frontend/filters'),
helpers = require('./core/frontend/helpers'),
// ## Custom Middleware
auth,
authAPI,
ghostLocals,
disableCachedResult,
// ## Custom Middleware
auth,
authAPI,
ghostLocals,
disableCachedResult,
// ## Variables
loading = when.defer(),
// ## Variables
loading = when.defer(),
/**
* Create new Ghost object
* @type {Ghost}
*/
ghost = new Ghost();
/**
* Create new Ghost object
* @type {Ghost}
*/
ghost = new Ghost();
ghost.app().configure('development', function () {
ghost.app().use(express.favicon(__dirname + '/content/images/favicon.ico'));
ghost.app().use(express.errorHandler({ dumpExceptions: true, showStack: true }));
ghost.app().use(express.logger('dev'));
ghost.app().use(I18n.load(ghost));
ghost.app().use(express.bodyParser({}));
ghost.app().use(express.cookieParser('try-ghost'));
ghost.app().use(express.cookieSession({ cookie: { maxAge: 60000000 }}));
ghost.app().use(ghost.initTheme(ghost.app()));
ghost.app().use(flash());
ghost.app().configure('development', function () {
ghost.app().use(express.favicon(__dirname + '/content/images/favicon.ico'));
ghost.app().use(express.errorHandler({ dumpExceptions: true, showStack: true }));
ghost.app().use(express.logger('dev'));
ghost.app().use(I18n.load(ghost));
ghost.app().use(express.bodyParser({}));
ghost.app().use(express.cookieParser('try-ghost'));
ghost.app().use(express.cookieSession({ cookie: { maxAge: 60000000 }}));
ghost.app().use(ghost.initTheme(ghost.app()));
ghost.app().use(flash());
});
/**
* Authenticate a request by redirecting to login if not logged in
*
* @type {*}
*/
auth = function (req, res, next) {
if (!req.session.user) {
if (req.url && /^\/ghost\/?$/gi.test(req.url)) {
// TODO: Welcome message? Intro if no logins yet?
req.shutUpJsLint = true;
} else {
req.flash('warn', "Please login");
}
return res.redirect('/ghost/login/?redirect=' + encodeURIComponent(req.path));
}
next();
};
/**
* Authenticate a request by responding with a 401 and json error details
*
* @type {*}
*/
authAPI = function (req, res, next) {
if (!req.session.user) {
// TODO: standardize error format/codes/messages
var err = { code: 42, message: 'Please login' };
res.json(401, { error: err });
return;
}
next();
};
/**
* Expose the standard locals that every external page should have available;
* path, navItems and settingsCache
*/
ghostLocals = function (req, res, next) {
ghost.doFilter('ghostNavItems', {path: req.path, navItems: []}, function (navData) {
// Make sure we have a locals value.
res.locals = res.locals || {};
// Extend it with nav data and settings
_.extend(res.locals, navData, {
messages: req.flash(),
settings: ghost.settings(),
availableThemes: ghost.paths().availableThemes,
availablePlugins: ghost.paths().availablePlugins
});
next();
});
};
disableCachedResult = function (req, res, next) {
res.set({
"Cache-Control": "no-cache, must-revalidate",
"Expires": "Sat, 26 Jul 1997 05:00:00 GMT"
});
/**
* Authenticate a request by redirecting to login if not logged in
*
* @type {*}
*/
auth = function (req, res, next) {
if (!req.session.user) {
if (req.url && /^\/ghost\/?$/gi.test(req.url)) {
// TODO: Welcome message? Intro if no logins yet?
req.shutUpJsLint = true;
} else {
req.flash('warn', "Please login");
}
next();
};
return res.redirect('/ghost/login/?redirect=' + encodeURIComponent(req.path));
}
// Expose the promise we will resolve after our pre-loading
ghost.loaded = loading.promise;
next();
};
when.all([ghost.init(), filters.loadCoreFilters(ghost), helpers.loadCoreHelpers(ghost)]).then(function () {
// post init config
ghost.app().use(ghostLocals);
/**
* Authenticate a request by responding with a 401 and json error details
*
* @type {*}
* API routes..
* @todo auth should be public auth not user auth
*/
authAPI = function (req, res, next) {
if (!req.session.user) {
// TODO: standardize error format/codes/messages
var err = { code: 42, message: 'Please login' };
res.json(401, { error: err });
return;
}
next();
};
ghost.app().get('/api/v0.1/posts', authAPI, disableCachedResult, api.requestHandler(api.posts.browse));
ghost.app().post('/api/v0.1/posts', authAPI, disableCachedResult, api.requestHandler(api.posts.add));
ghost.app().get('/api/v0.1/posts/:id', authAPI, disableCachedResult, api.requestHandler(api.posts.read));
ghost.app().put('/api/v0.1/posts/:id', authAPI, disableCachedResult, api.requestHandler(api.posts.edit));
ghost.app().del('/api/v0.1/posts/:id', authAPI, disableCachedResult, api.requestHandler(api.posts.destroy));
ghost.app().get('/api/v0.1/settings', authAPI, disableCachedResult, api.cachedSettingsRequestHandler(api.settings.browse));
ghost.app().get('/api/v0.1/settings/:key', authAPI, disableCachedResult, api.cachedSettingsRequestHandler(api.settings.read));
ghost.app().put('/api/v0.1/settings', authAPI, disableCachedResult, api.cachedSettingsRequestHandler(api.settings.edit));
/**
* Expose the standard locals that every external page should have available;
* path, navItems and settingsCache
* Admin routes..
* @todo put these somewhere in admin
*/
ghostLocals = function (req, res, next) {
ghost.doFilter('ghostNavItems', {path: req.path, navItems: []}, function (navData) {
// Make sure we have a locals value.
res.locals = res.locals || {};
ghost.app().get(/^\/logout\/?$/, admin.logout);
ghost.app().get('/ghost/login/', admin.login);
ghost.app().get('/ghost/signup/', admin.signup);
ghost.app().post('/ghost/login/', admin.auth);
ghost.app().post('/ghost/signup/', admin.doRegister);
ghost.app().get('/ghost/editor/:id', auth, admin.editor);
ghost.app().get('/ghost/editor', auth, admin.editor);
ghost.app().get('/ghost/content', auth, admin.content);
ghost.app().get('/ghost/settings*', auth, admin.settings);
ghost.app().get('/ghost/debug', auth, admin.debug.index);
ghost.app().get('/ghost/debug/db/export/', auth, admin.debug['export']);
ghost.app().post('/ghost/debug/db/import/', auth, admin.debug.import);
ghost.app().get('/ghost/debug/db/reset/', auth, admin.debug.reset);
ghost.app().get(/^\/(ghost$|(ghost-admin|admin|wp-admin|dashboard|login)\/?)/, auth, function (req, res) {
res.redirect('/ghost/');
});
ghost.app().get('/ghost/', auth, admin.index);
// Extend it with nav data and settings
_.extend(res.locals, navData, {
messages: req.flash(),
settings: ghost.settings(),
availableThemes: ghost.paths().availableThemes,
availablePlugins: ghost.paths().availablePlugins
});
/**
* Frontend routes..
* @todo dynamic routing, homepage generator, filters ETC ETC
*/
ghost.app().get('/:slug', frontend.single);
ghost.app().get('/', frontend.homepage);
next();
});
};
ghost.app().listen(3333, function () {
console.log("Express server listening on port " + 3333);
disableCachedResult = function (req, res, next) {
res.set({
"Cache-Control": "no-cache, must-revalidate",
"Expires": "Sat, 26 Jul 1997 05:00:00 GMT"
});
// Let everyone know we have finished loading
loading.resolve();
});
next();
};
// Expose the promise we will resolve after our pre-loading
ghost.loaded = loading.promise;
when.all([ghost.init(), filters.loadCoreFilters(ghost), helpers.loadCoreHelpers(ghost)]).then(function () {
// post init config
ghost.app().use(ghostLocals);
/**
* API routes..
* @todo auth should be public auth not user auth
*/
ghost.app().get('/api/v0.1/posts', authAPI, disableCachedResult, api.requestHandler(api.posts.browse));
ghost.app().post('/api/v0.1/posts', authAPI, disableCachedResult, api.requestHandler(api.posts.add));
ghost.app().get('/api/v0.1/posts/:id', authAPI, disableCachedResult, api.requestHandler(api.posts.read));
ghost.app().put('/api/v0.1/posts/:id', authAPI, disableCachedResult, api.requestHandler(api.posts.edit));
ghost.app().del('/api/v0.1/posts/:id', authAPI, disableCachedResult, api.requestHandler(api.posts.destroy));
ghost.app().get('/api/v0.1/settings', authAPI, disableCachedResult, api.cachedSettingsRequestHandler(api.settings.browse));
ghost.app().get('/api/v0.1/settings/:key', authAPI, disableCachedResult, api.cachedSettingsRequestHandler(api.settings.read));
ghost.app().put('/api/v0.1/settings', authAPI, disableCachedResult, api.cachedSettingsRequestHandler(api.settings.edit));
/**
* Admin routes..
* @todo put these somewhere in admin
*/
ghost.app().get(/^\/logout\/?$/, admin.logout);
ghost.app().get('/ghost/login/', admin.login);
ghost.app().get('/ghost/signup/', admin.signup);
ghost.app().post('/ghost/login/', admin.auth);
ghost.app().post('/ghost/signup/', admin.doRegister);
ghost.app().get('/ghost/editor/:id', auth, admin.editor);
ghost.app().get('/ghost/editor', auth, admin.editor);
ghost.app().get('/ghost/content', auth, admin.content);
ghost.app().get('/ghost/settings*', auth, admin.settings);
ghost.app().get('/ghost/debug', auth, admin.debug.index);
ghost.app().get('/ghost/debug/db/export/', auth, admin.debug['export']);
ghost.app().post('/ghost/debug/db/import/', auth, admin.debug.import);
ghost.app().get('/ghost/debug/db/reset/', auth, admin.debug.reset);
ghost.app().get(/^\/(ghost$|(ghost-admin|admin|wp-admin|dashboard|login)\/?)/, auth, function (req, res) {
res.redirect('/ghost/');
});
ghost.app().get('/ghost/', auth, admin.index);
/**
* Frontend routes..
* @todo dynamic routing, homepage generator, filters ETC ETC
*/
ghost.app().get('/:slug', frontend.single);
ghost.app().get('/', frontend.homepage);
ghost.app().listen(3333, function () {
console.log("Express server listening on port " + 3333);
// Let everyone know we have finished loading
loading.resolve();
});
}, errors.logAndThrowError);
}());
}, errors.logAndThrowError);

162
config.js
View File

@ -3,104 +3,100 @@
/**
* global module
**/
(function () {
"use strict";
var path = require('path'),
config;
var path = require('path'),
config;
/**
* @module config
* @type {Object}
*/
config = {};
/**
* @module config
* @type {Object}
*/
config = {};
// ## Admin settings
// ## Admin settings
/**
* @property {string} defaultLang
*/
config.defaultLang = 'en';
/**
* @property {string} defaultLang
*/
config.defaultLang = 'en';
/**
* @property {boolean} forceI18n
*/
config.forceI18n = true;
/**
* @property {boolean} forceI18n
*/
config.forceI18n = true;
// ## Themes
// ## Themes
/**
* @property {string} themeDir
*/
/**
* @property {string} themeDir
*/
// Themes
config.themeDir = 'themes';
// Themes
config.themeDir = 'themes';
/**
* @property {string} activeTheme
*/
config.activeTheme = 'casper';
/**
* @property {string} activeTheme
*/
config.activeTheme = 'casper';
// ## Homepage settings
/**
* @module homepage
* @type {Object}
*/
config.homepage = {};
// ## Homepage settings
/**
* @module homepage
* @type {Object}
*/
config.homepage = {};
/**
* @property {number} features
*/
config.homepage.features = 1;
/**
* @property {number} features
*/
config.homepage.features = 1;
/**
* @property {number} posts
*/
config.homepage.posts = 4;
/**
* @property {number} posts
*/
config.homepage.posts = 4;
config.database = {
testing: {
client: 'sqlite3',
connection: {
filename: path.join(__dirname, '/core/shared/data/tests.db')
}
},
config.database = {
testing: {
client: 'sqlite3',
connection: {
filename: path.join(__dirname, '/core/shared/data/tests.db')
}
travis: {
client: 'sqlite3',
connection: {
filename: path.join(__dirname, '/core/shared/data/tests.db')
}
// debug: true
},
development: {
client: 'sqlite3',
connection: {
filename: path.join(__dirname, '/core/shared/data/testdb.db')
},
debug: false
// debug: true
},
travis: {
client: 'sqlite3',
connection: {
filename: path.join(__dirname, '/core/shared/data/tests.db')
}
// debug: true
},
staging: {},
development: {
client: 'sqlite3',
connection: {
filename: path.join(__dirname, '/core/shared/data/testdb.db')
},
debug: false
// debug: true
},
production: {}
};
staging: {},
/**
* @property {Array} nav
*/
config.nav = [{
title: 'Home',
url: '/'
}, {
title: 'Admin',
url: '/ghost'
}];
production: {}
};
/**
* @property {Array} nav
*/
config.nav = [{
title: 'Home',
url: '/'
}, {
title: 'Admin',
url: '/ghost'
}];
/**
* @property {Object} exports
*/
module.exports = config;
}());
/**
* @property {Object} exports
*/
module.exports = config;

View File

@ -1,273 +1,268 @@
/*global require, module */
(function () {
"use strict";
var Ghost = require('../../ghost'),
dataExport = require('../../shared/data/export'),
dataImport = require('../../shared/data/import'),
_ = require('underscore'),
fs = require('fs'),
path = require('path'),
when = require('when'),
nodefn = require('when/node/function'),
api = require('../../shared/api'),
var Ghost = require('../../ghost'),
dataExport = require('../../shared/data/export'),
dataImport = require('../../shared/data/import'),
_ = require('underscore'),
fs = require('fs'),
path = require('path'),
when = require('when'),
nodefn = require('when/node/function'),
api = require('../../shared/api'),
ghost = new Ghost(),
dataProvider = ghost.dataProvider,
adminNavbar,
adminControllers;
ghost = new Ghost(),
dataProvider = ghost.dataProvider,
adminNavbar,
adminControllers;
// TODO: combine path/navClass to single "slug(?)" variable with no prefix
adminNavbar = {
dashboard: {
name: 'Dashboard',
navClass: 'dashboard',
key: 'admin.navbar.dashboard',
// defaultString: 'dashboard',
path: '/'
},
content: {
name: 'Content',
navClass: 'content',
key: 'admin.navbar.content',
// defaultString: 'content',
path: '/content/'
},
add: {
name: 'New Post',
navClass: 'editor',
key: 'admin.navbar.editor',
// defaultString: 'editor',
path: '/editor/'
},
settings: {
name: 'Settings',
navClass: 'settings',
key: 'admin.navbar.settings',
// defaultString: 'settings',
path: '/settings/'
}
};
ghost.doFilter('messWithAdmin', adminNavbar, function () {
console.log('the dofilter hook called in /core/admin/controllers/index.js');
});
// TODO - make this a util or helper
function setSelected(list, name) {
_.each(list, function (item, key) {
item.selected = key === name;
});
return list;
// TODO: combine path/navClass to single "slug(?)" variable with no prefix
adminNavbar = {
dashboard: {
name: 'Dashboard',
navClass: 'dashboard',
key: 'admin.navbar.dashboard',
// defaultString: 'dashboard',
path: '/'
},
content: {
name: 'Content',
navClass: 'content',
key: 'admin.navbar.content',
// defaultString: 'content',
path: '/content/'
},
add: {
name: 'New Post',
navClass: 'editor',
key: 'admin.navbar.editor',
// defaultString: 'editor',
path: '/editor/'
},
settings: {
name: 'Settings',
navClass: 'settings',
key: 'admin.navbar.settings',
// defaultString: 'settings',
path: '/settings/'
}
};
adminControllers = {
'login': function (req, res) {
res.render('login', {
bodyClass: 'ghost-login',
hideNavbar: true,
adminNav: setSelected(adminNavbar, 'login')
});
},
'auth': function (req, res) {
api.users.check({email: req.body.email, pw: req.body.password}).then(function (user) {
console.log('user found: ', user);
req.session.user = "ghostadmin";
res.redirect(req.query.redirect || '/ghost/');
}, function (error) {
// Do something here to signal the reason for an error
req.flash('error', error.message);
res.redirect('/ghost/login/');
});
},
'signup': function (req, res) {
res.render('signup', {
bodyClass: 'ghost-login',
hideNavbar: true,
adminNav: setSelected(adminNavbar, 'login')
});
},
'doRegister': function (req, res) {
var email = req.body.email_address,
password = req.body.password;
ghost.doFilter('messWithAdmin', adminNavbar, function () {
console.log('the dofilter hook called in /core/admin/controllers/index.js');
});
if (email !== '' && password.length > 5) {
api.users.add({
email_address: email,
password: password
}).then(function (user) {
console.log('user added', user);
res.redirect('/ghost/login/');
}, function (error) {
req.flash('error', error.message);
res.redirect('/ghost/signup/');
});
} else {
req.flash('error', "The password is too short. Have at least 6 characters in there");
res.redirect('back');
}
},
'logout': function (req, res) {
delete req.session.user;
req.flash('success', "You were successfully logged out");
// TODO - make this a util or helper
function setSelected(list, name) {
_.each(list, function (item, key) {
item.selected = key === name;
});
return list;
}
adminControllers = {
'login': function (req, res) {
res.render('login', {
bodyClass: 'ghost-login',
hideNavbar: true,
adminNav: setSelected(adminNavbar, 'login')
});
},
'auth': function (req, res) {
api.users.check({email: req.body.email, pw: req.body.password}).then(function (user) {
console.log('user found: ', user);
req.session.user = "ghostadmin";
res.redirect(req.query.redirect || '/ghost/');
}, function (error) {
// Do something here to signal the reason for an error
req.flash('error', error.message);
res.redirect('/ghost/login/');
},
'index': function (req, res) {
res.render('dashboard', {
bodyClass: 'dashboard',
adminNav: setSelected(adminNavbar, 'dashboard')
});
},
'signup': function (req, res) {
res.render('signup', {
bodyClass: 'ghost-login',
hideNavbar: true,
adminNav: setSelected(adminNavbar, 'login')
});
},
'doRegister': function (req, res) {
var email = req.body.email_address,
password = req.body.password;
if (email !== '' && password.length > 5) {
api.users.add({
email_address: email,
password: password
}).then(function (user) {
console.log('user added', user);
res.redirect('/ghost/login/');
}, function (error) {
req.flash('error', error.message);
res.redirect('/ghost/signup/');
});
} else {
req.flash('error', "The password is too short. Have at least 6 characters in there");
res.redirect('back');
}
},
'logout': function (req, res) {
delete req.session.user;
req.flash('success', "You were successfully logged out");
res.redirect('/ghost/login/');
},
'index': function (req, res) {
res.render('dashboard', {
bodyClass: 'dashboard',
adminNav: setSelected(adminNavbar, 'dashboard')
});
},
'editor': function (req, res) {
if (req.params.id !== undefined) {
api.posts.read({id: parseInt(req.params.id, 10)})
.then(function (post) {
res.render('editor', {
bodyClass: 'editor',
adminNav: setSelected(adminNavbar, 'content'),
title: post.get('title'),
content: post.get('content')
});
});
} else {
res.render('editor', {
bodyClass: 'editor',
adminNav: setSelected(adminNavbar, 'add')
});
}
},
'content': function (req, res) {
api.posts.browse({status: req.params.status || 'all'})
.then(function (page) {
res.render('content', {
bodyClass: 'manage',
adminNav: setSelected(adminNavbar, 'content'),
posts: page.posts
});
});
},
'settings': function (req, res) {
api.settings.browse()
.then(function (settings) {
res.render('settings', {
bodyClass: 'settings',
adminNav: setSelected(adminNavbar, 'settings'),
settings: settings
});
});
},
'debug': { /* ugly temporary stuff for managing the app before it's properly finished */
index: function (req, res) {
res.render('debug', {
bodyClass: 'settings',
adminNav: setSelected(adminNavbar, 'settings')
});
},
'editor': function (req, res) {
if (req.params.id !== undefined) {
api.posts.read({id: parseInt(req.params.id, 10)})
.then(function (post) {
res.render('editor', {
bodyClass: 'editor',
adminNav: setSelected(adminNavbar, 'content'),
title: post.get('title'),
content: post.get('content')
});
});
} else {
res.render('editor', {
bodyClass: 'editor',
adminNav: setSelected(adminNavbar, 'add')
});
}
},
'content': function (req, res) {
api.posts.browse({status: req.params.status || 'all'})
.then(function (page) {
res.render('content', {
bodyClass: 'manage',
adminNav: setSelected(adminNavbar, 'content'),
posts: page.posts
});
});
},
'settings': function (req, res) {
api.settings.browse()
.then(function (settings) {
res.render('settings', {
bodyClass: 'settings',
adminNav: setSelected(adminNavbar, 'settings'),
settings: settings
});
});
},
'debug': { /* ugly temporary stuff for managing the app before it's properly finished */
index: function (req, res) {
res.render('debug', {
bodyClass: 'settings',
adminNav: setSelected(adminNavbar, 'settings')
});
},
'export': function (req, res) {
// Get current version from settings
api.settings.read({ key: "currentVersion" })
.then(function (setting) {
// Export the current versions data
return dataExport(setting.value);
}, function () {
// If no setting, assume 001
return dataExport("001");
})
.then(function (exportedData) {
// Save the exported data to the file system for download
var fileName = path.resolve(__dirname + '/../../shared/data/export/exported-' + (new Date().getTime()) + '.json');
'export': function (req, res) {
// Get current version from settings
api.settings.read({ key: "currentVersion" })
.then(function (setting) {
// Export the current versions data
return dataExport(setting.value);
}, function () {
// If no setting, assume 001
return dataExport("001");
})
.then(function (exportedData) {
// Save the exported data to the file system for download
var fileName = path.resolve(__dirname + '/../../shared/data/export/exported-' + (new Date().getTime()) + '.json');
return nodefn.call(fs.writeFile, fileName, JSON.stringify(exportedData)).then(function () {
return when(fileName);
});
})
.then(function (exportedFilePath) {
// Send the exported data file
res.download(exportedFilePath, 'GhostData.json');
})
.otherwise(function (error) {
// Notify of an error if it occurs
req.flash("error", error.message || error);
res.redirect("/ghost/debug");
return nodefn.call(fs.writeFile, fileName, JSON.stringify(exportedData)).then(function () {
return when(fileName);
});
},
'import': function (req, res) {
if (!req.files.importfile) {
})
.then(function (exportedFilePath) {
// Send the exported data file
res.download(exportedFilePath, 'GhostData.json');
})
.otherwise(function (error) {
// Notify of an error if it occurs
req.flash("error", "Must select a file to import");
return res.redirect("/ghost/debug");
}
// Get the current version for importing
api.settings.read({ key: "currentVersion" })
.then(function (setting) {
return when(setting.value);
}, function () {
return when("001");
})
.then(function (currentVersion) {
// Read the file contents
return nodefn.call(fs.readFile, req.files.importfile.path)
.then(function (fileContents) {
var importData;
// Parse the json data
try {
importData = JSON.parse(fileContents);
} catch (e) {
return when.reject(new Error("Failed to parse the import file"));
}
if (!importData.meta || !importData.meta.version) {
return when.reject(new Error("Import data does not specify version"));
}
// Import for the current version
return dataImport(currentVersion, importData);
});
})
.then(function () {
req.flash("success", "Data imported");
})
.otherwise(function (error) {
// Notify of an error if it occurs
req.flash("error", error.message || error);
})
.then(function () {
res.redirect("/ghost/debug");
});
},
'reset': function (req, res) {
// Grab the current version so we can get the migration
api.settings.read({ key: "currentVersion" })
.then(function (setting) {
var migration = require("../../shared/data/migration/" + setting.value);
// Run the downward migration
return migration.down();
}, function () {
// If no version in the DB, assume 001
var migration = require("../../shared/data/migration/001");
// Run the downward migration
return migration.down();
})
.then(function () {
// Re-initalize the providers (should run the migration up again)
return dataProvider.init();
})
.then(function () {
req.flash("success", "Database reset");
})
.otherwise(function (error) {
req.flash("error", error.message || error);
})
.then(function () {
res.redirect('/ghost/debug');
});
req.flash("error", error.message || error);
res.redirect("/ghost/debug");
});
},
'import': function (req, res) {
if (!req.files.importfile) {
// Notify of an error if it occurs
req.flash("error", "Must select a file to import");
return res.redirect("/ghost/debug");
}
}
};
module.exports = adminControllers;
}());
// Get the current version for importing
api.settings.read({ key: "currentVersion" })
.then(function (setting) {
return when(setting.value);
}, function () {
return when("001");
})
.then(function (currentVersion) {
// Read the file contents
return nodefn.call(fs.readFile, req.files.importfile.path)
.then(function (fileContents) {
var importData;
// Parse the json data
try {
importData = JSON.parse(fileContents);
} catch (e) {
return when.reject(new Error("Failed to parse the import file"));
}
if (!importData.meta || !importData.meta.version) {
return when.reject(new Error("Import data does not specify version"));
}
// Import for the current version
return dataImport(currentVersion, importData);
});
})
.then(function () {
req.flash("success", "Data imported");
})
.otherwise(function (error) {
// Notify of an error if it occurs
req.flash("error", error.message || error);
})
.then(function () {
res.redirect("/ghost/debug");
});
},
'reset': function (req, res) {
// Grab the current version so we can get the migration
api.settings.read({ key: "currentVersion" })
.then(function (setting) {
var migration = require("../../shared/data/migration/" + setting.value);
// Run the downward migration
return migration.down();
}, function () {
// If no version in the DB, assume 001
var migration = require("../../shared/data/migration/001");
// Run the downward migration
return migration.down();
})
.then(function () {
// Re-initalize the providers (should run the migration up again)
return dataProvider.init();
})
.then(function () {
req.flash("success", "Database reset");
})
.otherwise(function (error) {
req.flash("error", error.message || error);
})
.then(function () {
res.redirect('/ghost/debug');
});
}
}
};
module.exports = adminControllers;

View File

@ -3,31 +3,28 @@
*/
/*global require, module */
(function () {
'use strict';
var Ghost = require('../../ghost'),
api = require('../../shared/api'),
var Ghost = require('../../ghost'),
api = require('../../shared/api'),
ghost = new Ghost(),
frontendControllers;
ghost = new Ghost(),
frontendControllers;
frontendControllers = {
'homepage': function (req, res) {
api.posts.browse().then(function (page) {
ghost.doFilter('prePostsRender', page.posts, function (posts) {
res.render('index', {posts: posts});
});
frontendControllers = {
'homepage': function (req, res) {
api.posts.browse().then(function (page) {
ghost.doFilter('prePostsRender', page.posts, function (posts) {
res.render('index', {posts: posts});
});
},
'single': function (req, res) {
api.posts.read({'slug': req.params.slug}).then(function (post) {
ghost.doFilter('prePostsRender', post.toJSON(), function (post) {
res.render('single', {post: post});
});
});
},
'single': function (req, res) {
api.posts.read({'slug': req.params.slug}).then(function (post) {
ghost.doFilter('prePostsRender', post.toJSON(), function (post) {
res.render('single', {post: post});
});
}
};
});
}
};
module.exports = frontendControllers;
}());
module.exports = frontendControllers;

View File

@ -1,31 +1,26 @@
(function () {
"use strict";
var _ = require('underscore'),
defaultCoreFilterPriority = 4,
coreFilters;
var _ = require('underscore'),
defaultCoreFilterPriority = 4,
coreFilters;
coreFilters = function (ghost) {
ghost.registerFilter('ghostNavItems', defaultCoreFilterPriority, function (args) {
var selectedItem;
coreFilters = function (ghost) {
ghost.registerFilter('ghostNavItems', defaultCoreFilterPriority, function (args) {
var selectedItem;
// Set the nav items based on the config
args.navItems = ghost.config().nav;
// Set the nav items based on the config
args.navItems = ghost.config().nav;
// Mark the current selected Item
selectedItem = _.find(args.navItems, function (item) {
// TODO: Better selection determination?
return item.url === args.path;
});
if (selectedItem) {
selectedItem.active = true;
}
return args;
// Mark the current selected Item
selectedItem = _.find(args.navItems, function (item) {
// TODO: Better selection determination?
return item.url === args.path;
});
};
module.exports.loadCoreFilters = coreFilters;
if (selectedItem) {
selectedItem.active = true;
}
}());
return args;
});
};
module.exports.loadCoreFilters = coreFilters;

View File

@ -1,53 +1,49 @@
(function () {
"use strict";
var fs = require('fs'),
path = require('path'),
_ = require('underscore'),
handlebars = require('express-hbs').handlebars,
nodefn = require('when/node/function'),
GhostNavHelper;
var fs = require('fs'),
path = require('path'),
_ = require('underscore'),
handlebars = require('express-hbs').handlebars,
nodefn = require('when/node/function'),
GhostNavHelper;
GhostNavHelper = function (navTemplate) {
// Bind the context for our methods.
_.bindAll(this, 'compileTemplate', 'renderNavItems');
GhostNavHelper = function (navTemplate) {
// Bind the context for our methods.
_.bindAll(this, 'compileTemplate', 'renderNavItems');
if (_.isFunction(navTemplate)) {
this.navTemplateFunc = navTemplate;
} else {
this.navTemplatePath = navTemplate;
}
};
if (_.isFunction(navTemplate)) {
this.navTemplateFunc = navTemplate;
} else {
this.navTemplatePath = navTemplate;
}
};
GhostNavHelper.prototype.compileTemplate = function (templatePath) {
var self = this;
GhostNavHelper.prototype.compileTemplate = function (templatePath) {
var self = this;
// Allow people to overwrite the navTemplatePath
templatePath = templatePath || this.navTemplatePath;
// Allow people to overwrite the navTemplatePath
templatePath = templatePath || this.navTemplatePath;
return nodefn.call(fs.readFile, templatePath).then(function (navTemplateContents) {
// TODO: Can handlebars compile async?
self.navTemplateFunc = handlebars.compile(navTemplateContents.toString());
});
};
return nodefn.call(fs.readFile, templatePath).then(function (navTemplateContents) {
// TODO: Can handlebars compile async?
self.navTemplateFunc = handlebars.compile(navTemplateContents.toString());
});
};
GhostNavHelper.prototype.renderNavItems = function (navItems) {
var output;
GhostNavHelper.prototype.renderNavItems = function (navItems) {
var output;
output = this.navTemplateFunc({links: navItems});
output = this.navTemplateFunc({links: navItems});
return output;
};
return output;
};
// A static helper method for registering with ghost
GhostNavHelper.registerWithGhost = function (ghost) {
var templatePath = path.join(ghost.paths().frontendViews, 'nav.hbs'),
ghostNavHelper = new GhostNavHelper(templatePath);
// A static helper method for registering with ghost
GhostNavHelper.registerWithGhost = function (ghost) {
var templatePath = path.join(ghost.paths().frontendViews, 'nav.hbs'),
ghostNavHelper = new GhostNavHelper(templatePath);
return ghostNavHelper.compileTemplate().then(function () {
ghost.registerThemeHelper("ghostNav", ghostNavHelper.renderNavItems);
});
};
return ghostNavHelper.compileTemplate().then(function () {
ghost.registerThemeHelper("ghostNav", ghostNavHelper.renderNavItems);
});
};
module.exports = GhostNavHelper;
}());
module.exports = GhostNavHelper;

View File

@ -1,127 +1,121 @@
(function () {
"use strict";
var _ = require('underscore'),
moment = require('moment'),
when = require('when'),
navHelper = require('./ghostNav'),
hbs = require('express-hbs'),
coreHelpers;
var _ = require('underscore'),
moment = require('moment'),
when = require('when'),
navHelper = require('./ghostNav'),
hbs = require('express-hbs'),
coreHelpers;
coreHelpers = function (ghost) {
/**
* [ description]
* @todo ghost core helpers + a way for themes to register them
* @param {Object} context date object
* @param {*} block
* @return {Object} A Moment time / date object
*/
ghost.registerThemeHelper('dateFormat', function (context, block) {
var f = block.hash.format || "MMM Do, YYYY";
return moment(context).format(f);
});
coreHelpers = function (ghost) {
/**
* [ description]
* @todo ghost core helpers + a way for themes to register them
* @param {Object} context date object
* @param {*} block
* @return {Object} A Moment time / date object
*/
ghost.registerThemeHelper('dateFormat', function (context, block) {
var f = block.hash.format || "MMM Do, YYYY";
return moment(context).format(f);
});
/**
* [ description]
*
* @param String key
* @param String default translation
* @param {Object} options
* @return String A correctly internationalised string
*/
ghost.registerThemeHelper('e', function (key, defaultString, options) {
var output;
/**
* [ description]
*
* @param String key
* @param String default translation
* @param {Object} options
* @return String A correctly internationalised string
*/
ghost.registerThemeHelper('e', function (key, defaultString, options) {
var output;
if (ghost.config().defaultLang === 'en' && _.isEmpty(options.hash) && !ghost.config().forceI18n) {
output = defaultString;
} else {
output = ghost.polyglot().t(key, options.hash);
}
if (ghost.config().defaultLang === 'en' && _.isEmpty(options.hash) && !ghost.config().forceI18n) {
output = defaultString;
return output;
});
ghost.registerThemeHelper('json', function (object, options) {
return JSON.stringify(object);
});
ghost.registerThemeHelper('foreach', function (context, options) {
var fn = options.fn,
inverse = options.inverse,
i = 0,
j = 0,
columns = options.hash.columns,
key,
ret = "",
data;
if (options.data) {
data = hbs.handlebars.createFrame(options.data);
}
function setKeys(_data, _i, _j, _columns) {
if (_i === 0) {
_data.first = true;
}
if (_i === _j - 1) {
_data.last = true;
}
// first post is index zero but still needs to be odd
if (_i % 2 === 1) {
_data.even = true;
} else {
output = ghost.polyglot().t(key, options.hash);
_data.odd = true;
}
return output;
});
ghost.registerThemeHelper('json', function (object, options) {
return JSON.stringify(object);
});
ghost.registerThemeHelper('foreach', function (context, options) {
var fn = options.fn,
inverse = options.inverse,
i = 0,
j = 0,
columns = options.hash.columns,
key,
ret = "",
data;
if (options.data) {
data = hbs.handlebars.createFrame(options.data);
if (_i % _columns === 0) {
_data.rowStart = true;
} else if (_i % _columns === (_columns - 1)) {
_data.rowEnd = true;
}
function setKeys(_data, _i, _j, _columns) {
if (_i === 0) {
_data.first = true;
return _data;
}
if (context && typeof context === 'object') {
if (context instanceof Array) {
for (j = context.length; i < j; i += 1) {
if (data) {
data.index = i;
data.first = data.rowEnd = data.rowStart = data.last = data.even = data.odd = false;
data = setKeys(data, i, j, columns);
}
ret = ret + fn(context[i], { data: data });
}
if (_i === _j - 1) {
_data.last = true;
} else {
for (key in context) {
if (context.hasOwnProperty(key)) {
j += 1;
}
}
// first post is index zero but still needs to be odd
if (_i % 2 === 1) {
_data.even = true;
} else {
_data.odd = true;
}
if (_i % _columns === 0) {
_data.rowStart = true;
} else if (_i % _columns === (_columns - 1)) {
_data.rowEnd = true;
}
return _data;
}
if (context && typeof context === 'object') {
if (context instanceof Array) {
for (j = context.length; i < j; i += 1) {
for (key in context) {
if (context.hasOwnProperty(key)) {
if (data) {
data.index = i;
data.key = key;
data.first = data.rowEnd = data.rowStart = data.last = data.even = data.odd = false;
data = setKeys(data, i, j, columns);
}
ret = ret + fn(context[i], { data: data });
}
} else {
for (key in context) {
if (context.hasOwnProperty(key)) {
j += 1;
}
}
for (key in context) {
if (context.hasOwnProperty(key)) {
if (data) {
data.key = key;
data.first = data.rowEnd = data.rowStart = data.last = data.even = data.odd = false;
data = setKeys(data, i, j, columns);
}
ret = ret + fn(context[key], {data: data});
i += 1;
}
ret = ret + fn(context[key], {data: data});
i += 1;
}
}
}
}
if (i === 0) {
ret = inverse(this);
}
return ret;
});
if (i === 0) {
ret = inverse(this);
}
return ret;
});
return when.all([
// Just one async helper for now, but could be more in the future
navHelper.registerWithGhost(ghost)
]);
};
module.exports.loadCoreHelpers = coreHelpers;
}());
return when.all([
// Just one async helper for now, but could be more in the future
navHelper.registerWithGhost(ghost)
]);
};
module.exports.loadCoreHelpers = coreHelpers;

View File

@ -1,257 +1,252 @@
// # Ghost Module
// Defines core methods required to build the frontend
/*global module, require, __dirname */
(function () {
"use strict";
// ## Setup Prerequisites
var config = require('./../config'),
when = require('when'),
express = require('express'),
errors = require('../core/shared/errorHandling'),
path = require('path'),
hbs = require('express-hbs'),
_ = require('underscore'),
Polyglot = require('node-polyglot'),
// ## Setup Prerequisites
var config = require('./../config'),
when = require('when'),
express = require('express'),
errors = require('../core/shared/errorHandling'),
path = require('path'),
hbs = require('express-hbs'),
_ = require('underscore'),
Polyglot = require('node-polyglot'),
models = require('./shared/models'),
ExampleFilter = require('../content/plugins/exampleFilters'),
models = require('./shared/models'),
ExampleFilter = require('../content/plugins/exampleFilters'),
requireTree = require('./shared/require-tree'),
themeDirectories = requireTree(path.resolve(__dirname + '../../content/themes')),
pluginDirectories = requireTree(path.resolve(__dirname + '../../content/plugins')),
requireTree = require('./shared/require-tree'),
themeDirectories = requireTree(path.resolve(__dirname + '../../content/themes')),
pluginDirectories = requireTree(path.resolve(__dirname + '../../content/plugins')),
Ghost,
instance,
defaults,
statuses;
Ghost,
instance,
defaults,
statuses;
// ## Default values
/**
* A hash of default values to use instead of 'magic' numbers/strings.
* @type {Object}
*/
defaults = {
filterPriority: 5,
maxPriority: 9
};
// ## Default values
/**
* A hash of default values to use instead of 'magic' numbers/strings.
* @type {Object}
*/
defaults = {
filterPriority: 5,
maxPriority: 9
};
// ## Article Statuses
/**
* A list of atricle status types
* @type {Object}
*/
statuses = {
'draft': 'draft',
'complete': 'complete',
'approved': 'approved',
'scheduled': 'scheduled',
'published': 'published'
};
// ## Article Statuses
/**
* A list of atricle status types
* @type {Object}
*/
statuses = {
'draft': 'draft',
'complete': 'complete',
'approved': 'approved',
'scheduled': 'scheduled',
'published': 'published'
};
// ## Module Methods
/**
* @method Ghost
* @returns {*}
* @constructor
*/
Ghost = function () {
var app,
plugin,
polyglot;
// ## Module Methods
/**
* @method Ghost
* @returns {*}
* @constructor
*/
Ghost = function () {
var app,
plugin,
polyglot;
if (!instance) {
instance = this;
if (!instance) {
instance = this;
// Holds the filters
instance.filterCallbacks = [];
// Holds the filters
instance.filterCallbacks = [];
// Holds the filter hooks (that are built in to Ghost Core)
instance.filters = [];
// Holds the filter hooks (that are built in to Ghost Core)
instance.filters = [];
// Holds the theme directories temporarily
instance.themeDirectories = {};
// Holds the theme directories temporarily
instance.themeDirectories = {};
// Holds the plugin directories temporarily
instance.pluginDirectories = {};
// Holds the plugin directories temporarily
instance.pluginDirectories = {};
plugin = new ExampleFilter(instance).init();
plugin = new ExampleFilter(instance).init();
app = express();
app = express();
polyglot = new Polyglot();
polyglot = new Polyglot();
// functionality
// load Plugins...
// var f = new FancyFirstChar(ghost).init();
// functionality
// load Plugins...
// var f = new FancyFirstChar(ghost).init();
_.extend(instance, {
app: function () { return app; },
config: function () { return config; },
_.extend(instance, {
app: function () { return app; },
config: function () { return config; },
// there's no management here to be sure this has loaded
settings: function () { return instance.settingsCache; },
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/',
'availableThemes': instance.themeDirectories,
'availablePlugins': instance.pluginDirectories
};
// there's no management here to be sure this has loaded
settings: function () { return instance.settingsCache; },
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/',
'availableThemes': instance.themeDirectories,
'availablePlugins': instance.pluginDirectories
};
}
});
}
return instance;
};
Ghost.prototype.init = function () {
var self = this;
return when.join(instance.dataProvider.init(), instance.getPaths()).then(function () {
return self.updateSettingsCache();
}, errors.logAndThrowError);
};
Ghost.prototype.updateSettingsCache = function (settings) {
var self = this;
settings = settings || {};
if (!_.isEmpty(settings)) {
self.settingsCache = settings;
} else {
// TODO: this should use api.browse
return models.Settings.findAll().then(function (result) {
var settings = {};
_.map(result.models, function (member) {
if (!settings.hasOwnProperty(member.attributes.key)) {
settings[member.attributes.key] = member.attributes.value;
}
});
}
return instance;
};
Ghost.prototype.init = function () {
var self = this;
return when.join(instance.dataProvider.init(), instance.getPaths()).then(function () {
return self.updateSettingsCache();
}, errors.logAndThrowError);
};
Ghost.prototype.updateSettingsCache = function (settings) {
var self = this;
settings = settings || {};
if (!_.isEmpty(settings)) {
self.settingsCache = settings;
} else {
// TODO: this should use api.browse
return models.Settings.findAll().then(function (result) {
var settings = {};
_.map(result.models, function (member) {
if (!settings.hasOwnProperty(member.attributes.key)) {
settings[member.attributes.key] = member.attributes.value;
}
});
}, errors.logAndThrowError);
}
};
self.settingsCache = settings;
}, errors.logAndThrowError);
}
};
/**
* @param {string} name
* @param {Function} fn
* @return {method} hbs.registerHelper
*/
Ghost.prototype.registerThemeHelper = function (name, fn) {
hbs.registerHelper(name, fn);
};
/**
* @param {string} name
* @param {Function} fn
* @return {method} hbs.registerHelper
*/
Ghost.prototype.registerThemeHelper = function (name, fn) {
hbs.registerHelper(name, fn);
};
/**
* @param {string} name
* @param {Function} fn
* @return {*}
*/
Ghost.prototype.registerTheme = function (name, fn) {
return this;
};
/**
* @param {string} name
* @param {Function} fn
* @return {*}
*/
Ghost.prototype.registerTheme = function (name, fn) {
return this;
};
/**
* @param {string} name
* @param {Function} fn
* @return {*}
*/
Ghost.prototype.registerPlugin = function (name, fn) {
return this;
};
/**
* @param {string} name
* @param {Function} fn
* @return {*}
*/
Ghost.prototype.registerPlugin = function (name, fn) {
return this;
};
/**
* @param {string} name
* @param {integer} priority
* @param {Function} fn
*/
Ghost.prototype.registerFilter = function (name, priority, fn) {
// Curry the priority optional parameter to a default of 5
if (_.isFunction(priority)) {
fn = priority;
priority = defaults.filterPriority;
}
/**
* @param {string} name
* @param {integer} priority
* @param {Function} fn
*/
Ghost.prototype.registerFilter = function (name, priority, fn) {
// Curry the priority optional parameter to a default of 5
if (_.isFunction(priority)) {
fn = priority;
priority = defaults.filterPriority;
this.filterCallbacks[name] = this.filterCallbacks[name] || {};
this.filterCallbacks[name][priority] = this.filterCallbacks[name][priority] || [];
this.filterCallbacks[name][priority].push(fn);
};
/**
* @param {string} name [description]
* @param {*} args
* @param {Function} callback
* @return {method} callback
*/
Ghost.prototype.doFilter = function (name, args, callback) {
var callbacks = this.filterCallbacks[name];
// Bug out early if no callbacks by that name
if (!callbacks) {
return callback(args);
}
_.times(defaults.maxPriority + 1, function (priority) {
// Bug out if no handlers on this priority
if (!_.isArray(callbacks[priority])) {
return;
}
this.filterCallbacks[name] = this.filterCallbacks[name] || {};
this.filterCallbacks[name][priority] = this.filterCallbacks[name][priority] || [];
this.filterCallbacks[name][priority].push(fn);
};
/**
* @param {string} name [description]
* @param {*} args
* @param {Function} callback
* @return {method} callback
*/
Ghost.prototype.doFilter = function (name, args, callback) {
var callbacks = this.filterCallbacks[name];
// Bug out early if no callbacks by that name
if (!callbacks) {
return callback(args);
}
_.times(defaults.maxPriority + 1, function (priority) {
// Bug out if no handlers on this priority
if (!_.isArray(callbacks[priority])) {
return;
}
// Call each handler for this priority level
_.each(callbacks[priority], function (filterHandler) {
args = filterHandler(args);
});
// Call each handler for this priority level
_.each(callbacks[priority], function (filterHandler) {
args = filterHandler(args);
});
});
callback(args);
callback(args);
};
/**
* Initialise Theme
*
* @todo Tod (?) Old comment
* @param {Object} app
*/
Ghost.prototype.initTheme = function (app) {
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'}
));
app.set('views', self.paths().activeTheme);
} else {
app.engine('hbs', hbs.express3({partialsDir: self.paths().adminViews + 'partials'}));
app.set('views', self.paths().adminViews);
app.use('/core/admin/assets', express['static'](path.join(__dirname, '/admin/assets')));
}
app.use(express['static'](self.paths().activeTheme));
app.use('/content/images', express['static'](path.join(__dirname, '/../content/images')));
next();
};
};
/**
* Initialise Theme
*
* @todo Tod (?) Old comment
* @param {Object} app
*/
Ghost.prototype.initTheme = function (app) {
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'}
));
app.set('views', self.paths().activeTheme);
} else {
app.engine('hbs', hbs.express3({partialsDir: self.paths().adminViews + 'partials'}));
app.set('views', self.paths().adminViews);
app.use('/core/admin/assets', express['static'](path.join(__dirname, '/admin/assets')));
}
app.use(express['static'](self.paths().activeTheme));
app.use('/content/images', express['static'](path.join(__dirname, '/../content/images')));
next();
};
};
// TODO: Expose the defaults for other people to see/manipulate as a static value?
// Ghost.defaults = defaults;
// TODO: Expose the defaults for other people to see/manipulate as a static value?
// Ghost.defaults = defaults;
module.exports = Ghost;
}());
module.exports = Ghost;

View File

@ -1,57 +1,53 @@
(function () {
"use strict";
var fs = require('fs'),
/**
* Create new Polyglot object
* @type {Polyglot}
*/
I18n;
var fs = require('fs'),
/**
* Create new Polyglot object
* @type {Polyglot}
*/
I18n;
I18n = function (ghost) {
I18n = function (ghost) {
// TODO: validate
var lang = ghost.config().defaultLang,
path = ghost.paths().lang,
langFilePath = path + lang + '.json';
// TODO: validate
var lang = ghost.config().defaultLang,
path = ghost.paths().lang,
langFilePath = path + lang + '.json';
return function (req, res, next) {
return function (req, res, next) {
if (lang === 'en') {
// TODO: do stuff here to optimise for en
if (lang === 'en') {
// TODO: do stuff here to optimise for en
// Make jslint empty block error go away
lang = 'en';
}
// Make jslint empty block error go away
/** TODO potentially use req.acceptedLanguages rather than the default
* TODO handle loading language file for frontend on frontend request etc
* TODO switch this mess to be promise driven */
fs.stat(langFilePath, function (error) {
if (error) {
console.log('No language file found for language ' + lang + '. Defaulting to en');
lang = 'en';
}
/** TODO potentially use req.acceptedLanguages rather than the default
* TODO handle loading language file for frontend on frontend request etc
* TODO switch this mess to be promise driven */
fs.stat(langFilePath, function (error) {
fs.readFile(langFilePath, function (error, data) {
if (error) {
console.log('No language file found for language ' + lang + '. Defaulting to en');
lang = 'en';
throw error;
}
fs.readFile(langFilePath, function (error, data) {
if (error) {
throw error;
}
try {
data = JSON.parse(data);
} catch (e) {
throw e; // TODO - do something better with the error here
}
try {
data = JSON.parse(data);
} catch (e) {
throw e; // TODO - do something better with the error here
}
ghost.polyglot().extend(data);
ghost.polyglot().extend(data);
next();
});
next();
});
};
});
};
};
module.exports.load = I18n;
}());
module.exports.load = I18n;

View File

@ -4,155 +4,149 @@
/**
* This is intended to replace the old dataProvider files and should access & manipulate the models directly
*/
var Ghost = require('../ghost'),
_ = require('underscore'),
when = require('when'),
errors = require('./errorHandling'),
/*global module, require */
(function () {
"use strict";
ghost = new Ghost(),
dataProvider = ghost.dataProvider,
posts,
users,
settings,
requestHandler,
cachedSettingsRequestHandler,
settingsObject,
settingsCollection;
var Ghost = require('../ghost'),
_ = require('underscore'),
when = require('when'),
errors = require('./errorHandling'),
// # Posts
posts = {
// takes filter / pagination parameters
// returns a page of posts in a json response
browse: function browse(options) {
return dataProvider.Post.findPage(options);
},
// takes an identifier (id or slug?)
// returns a single post in a json response
read: function read(args) {
return dataProvider.Post.findOne(args);
},
// takes a json object with all the properties which should be updated
// returns the resulting post in a json response
edit: function edit(postData) {
return dataProvider.Post.edit(postData);
},
// takes a json object representing a post,
// returns the resulting post in a json response
add: function add(postData) {
return dataProvider.Post.add(postData);
},
// takes an identifier (id or slug?)
// returns a json response with the id of the deleted post
destroy: function destroy(args) {
return dataProvider.Post.destroy(args.id);
}
};
ghost = new Ghost(),
dataProvider = ghost.dataProvider,
posts,
users,
settings,
requestHandler,
cachedSettingsRequestHandler,
settingsObject,
settingsCollection;
// # Users
users = {
add: function add(postData) {
return dataProvider.User.add(postData);
},
check: function check(postData) {
return dataProvider.User.check(postData);
}
};
// # Posts
posts = {
// takes filter / pagination parameters
// returns a page of posts in a json response
browse: function browse(options) {
return dataProvider.Post.findPage(options);
},
// takes an identifier (id or slug?)
// returns a single post in a json response
read: function read(args) {
return dataProvider.Post.findOne(args);
},
// takes a json object with all the properties which should be updated
// returns the resulting post in a json response
edit: function edit(postData) {
return dataProvider.Post.edit(postData);
},
// takes a json object representing a post,
// returns the resulting post in a json response
add: function add(postData) {
return dataProvider.Post.add(postData);
},
// takes an identifier (id or slug?)
// returns a json response with the id of the deleted post
destroy: function destroy(args) {
return dataProvider.Post.destroy(args.id);
}
};
// # Settings
// # Users
users = {
add: function add(postData) {
return dataProvider.User.add(postData);
},
check: function check(postData) {
return dataProvider.User.check(postData);
}
};
// Turn a settings collection into a single object/hashmap
settingsObject = function (settings) {
return (settings.toJSON ? settings.toJSON() : settings).reduce(function (res, item) {
if (item.toJSON) { item = item.toJSON(); }
if (item.key) { res[item.key] = item.value; }
return res;
}, {});
};
// Turn an object into a collection
settingsCollection = function (settings) {
return _.map(settings, function (value, key) {
return { key: key, value: value };
});
};
// # Settings
settings = {
browse: function browse(options) {
return dataProvider.Settings.browse(options).then(settingsObject, errors.logAndThrowError);
},
read: function read(options) {
return dataProvider.Settings.read(options.key).then(function (setting) {
if (!setting) {
return when.reject("Unable to find setting: " + options.key);
}
// Turn a settings collection into a single object/hashmap
settingsObject = function (settings) {
return (settings.toJSON ? settings.toJSON() : settings).reduce(function (res, item) {
if (item.toJSON) { item = item.toJSON(); }
if (item.key) { res[item.key] = item.value; }
return res;
}, {});
};
// Turn an object into a collection
settingsCollection = function (settings) {
return _.map(settings, function (value, key) {
return { key: key, value: value };
return _.pick(setting.toJSON(), 'key', 'value');
}, errors.logAndThrowError);
},
edit: function edit(settings) {
settings = settingsCollection(settings);
return dataProvider.Settings.edit(settings).then(settingsObject, errors.logAndThrowError);
}
};
// categories: {};
// post_categories: {};
// requestHandler
// decorator for api functions which are called via an HTTP request
// takes the API method and wraps it so that it gets data from the request and returns a sensible JSON response
requestHandler = function (apiMethod) {
return function (req, res) {
var options = _.extend(req.body, req.query, req.params);
return apiMethod(options).then(function (result) {
res.json(result || {});
}, function (error) {
res.json(400, {error: error});
});
};
};
settings = {
browse: function browse(options) {
return dataProvider.Settings.browse(options).then(settingsObject, errors.logAndThrowError);
},
read: function read(options) {
return dataProvider.Settings.read(options.key).then(function (setting) {
if (!setting) {
return when.reject("Unable to find setting: " + options.key);
}
cachedSettingsRequestHandler = function (apiMethod) {
if (!ghost.settings()) {
return requestHandler(apiMethod);
}
return _.pick(setting.toJSON(), 'key', 'value');
}, errors.logAndThrowError);
},
edit: function edit(settings) {
settings = settingsCollection(settings);
return dataProvider.Settings.edit(settings).then(settingsObject, errors.logAndThrowError);
}
};
return function (req, res) {
var options = _.extend(req.body, req.query, req.params),
promise;
// categories: {};
// post_categories: {};
// requestHandler
// decorator for api functions which are called via an HTTP request
// takes the API method and wraps it so that it gets data from the request and returns a sensible JSON response
requestHandler = function (apiMethod) {
return function (req, res) {
var options = _.extend(req.body, req.query, req.params);
return apiMethod(options).then(function (result) {
res.json(result || {});
}, function (error) {
res.json(400, {error: error});
switch (apiMethod.name) {
case 'browse':
promise = when(ghost.settings());
break;
case 'read':
promise = when(ghost.settings()[options.key]);
break;
case 'edit':
promise = apiMethod(options).then(function (result) {
ghost.updateSettingsCache(result);
return result;
});
};
};
cachedSettingsRequestHandler = function (apiMethod) {
if (!ghost.settings()) {
return requestHandler(apiMethod);
break;
default:
errors.logAndThrowError(new Error('Unknown method name for settings API: ' + apiMethod.name));
}
return function (req, res) {
var options = _.extend(req.body, req.query, req.params),
promise;
switch (apiMethod.name) {
case 'browse':
promise = when(ghost.settings());
break;
case 'read':
promise = when(ghost.settings()[options.key]);
break;
case 'edit':
promise = apiMethod(options).then(function (result) {
ghost.updateSettingsCache(result);
return result;
});
break;
default:
errors.logAndThrowError(new Error('Unknown method name for settings API: ' + apiMethod.name));
}
return promise.then(function (result) {
res.json(result || {});
}, function (error) {
res.json(400, {error: error});
});
};
return promise.then(function (result) {
res.json(result || {});
}, function (error) {
res.json(400, {error: error});
});
};
};
module.exports.posts = posts;
module.exports.users = users;
module.exports.settings = settings;
module.exports.requestHandler = requestHandler;
module.exports.cachedSettingsRequestHandler = cachedSettingsRequestHandler;
}());
module.exports.posts = posts;
module.exports.users = users;
module.exports.settings = settings;
module.exports.requestHandler = requestHandler;
module.exports.cachedSettingsRequestHandler = cachedSettingsRequestHandler;

View File

@ -1,49 +1,45 @@
(function () {
"use strict";
var _ = require("underscore"),
when = require("when"),
knex = require('../../models/base').Knex,
Exporter001;
var _ = require("underscore"),
when = require("when"),
knex = require('../../models/base').Knex,
Exporter001;
Exporter001 = function () {
this.version = "001";
};
Exporter001 = function () {
this.version = "001";
};
Exporter001.prototype.exportData = function () {
var self = this,
tables = ['posts', 'users', 'roles', 'roles_users', 'permissions', 'permissions_roles', 'settings'],
selectOps = _.map(tables, function (name) {
return knex(name).select();
});
return when.all(selectOps).then(function (tableData) {
var exportData = {
meta: {
exported_on: new Date().getTime(),
version: self.version
},
data: {
// Filled below
}
};
_.each(tables, function (name, i) {
exportData.data[name] = tableData[i];
});
return when.resolve(exportData);
}, function (err) {
console.log("Error exporting data: " + err);
Exporter001.prototype.exportData = function () {
var self = this,
tables = ['posts', 'users', 'roles', 'roles_users', 'permissions', 'permissions_roles', 'settings'],
selectOps = _.map(tables, function (name) {
return knex(name).select();
});
};
module.exports = {
// Make available for unit tests
Exporter001: Exporter001,
return when.all(selectOps).then(function (tableData) {
var exportData = {
meta: {
exported_on: new Date().getTime(),
version: self.version
},
data: {
// Filled below
}
};
exportData: function () {
return new Exporter001().exportData();
}
};
}());
_.each(tables, function (name, i) {
exportData.data[name] = tableData[i];
});
return when.resolve(exportData);
}, function (err) {
console.log("Error exporting data: " + err);
});
};
module.exports = {
// Make available for unit tests
Exporter001: Exporter001,
exportData: function () {
return new Exporter001().exportData();
}
};

View File

@ -1,21 +1,17 @@
(function () {
"use strict";
var when = require('when');
var when = require('when');
module.exports = function (version) {
var exporter;
module.exports = function (version) {
var exporter;
try {
exporter = require("./" + version);
} catch (ignore) {
// Zero effs given
}
try {
exporter = require("./" + version);
} catch (ignore) {
// Zero effs given
}
if (!exporter) {
return when.reject("No exporter found");
}
if (!exporter) {
return when.reject("No exporter found");
}
return exporter.exportData();
};
}());
return exporter.exportData();
};

View File

@ -1,4 +1,3 @@
var uuid = require('node-uuid');
/*global module */
@ -159,5 +158,4 @@ module.exports = {
"role_id": 1
}
]
};
};

View File

@ -1,58 +1,54 @@
(function () {
"use strict";
var when = require("when"),
_ = require("underscore"),
knex = require('../../models/base').Knex,
Importer001;
var when = require("when"),
_ = require("underscore"),
knex = require('../../models/base').Knex,
Importer001;
Importer001 = function () {
_.bindAll(this, "importFrom001");
Importer001 = function () {
_.bindAll(this, "importFrom001");
this.version = "001";
this.version = "001";
this.importFrom = {
"001": this.importFrom001
};
this.importFrom = {
"001": this.importFrom001
};
};
Importer001.prototype.importData = function (data) {
return this.canImport(data)
.then(function (importerFunc) {
return importerFunc(data);
}, function (reason) {
return when.reject(reason);
});
};
Importer001.prototype.canImport = function (data) {
if (data.meta && data.meta.version && this.importFrom[data.meta.version]) {
return when.resolve(this.importFrom[data.meta.version]);
}
return when.reject("Unsupported version of data");
};
Importer001.prototype.importFrom001 = function (data) {
var insertOps = [];
_.each(data.data, function (tableData, name) {
if (tableData && tableData.length) {
insertOps.push(knex(name).insert(tableData));
}
Importer001.prototype.importData = function (data) {
return this.canImport(data)
.then(function (importerFunc) {
return importerFunc(data);
}, function (reason) {
return when.reject(reason);
});
};
return when.all(insertOps).then(function (results) {
return when.resolve(results);
}, function (err) {
console.log("Error inserting imported data: ", err.message || err, err.stack);
});
};
Importer001.prototype.canImport = function (data) {
if (data.meta && data.meta.version && this.importFrom[data.meta.version]) {
return when.resolve(this.importFrom[data.meta.version]);
}
module.exports = {
Importer001: Importer001,
importData: function (data) {
new Importer001().importData(data);
return when.reject("Unsupported version of data");
};
Importer001.prototype.importFrom001 = function (data) {
var insertOps = [];
_.each(data.data, function (tableData, name) {
if (tableData && tableData.length) {
insertOps.push(knex(name).insert(tableData));
}
};
}());
});
return when.all(insertOps).then(function (results) {
return when.resolve(results);
}, function (err) {
console.log("Error inserting imported data: ", err.message || err, err.stack);
});
};
module.exports = {
Importer001: Importer001,
importData: function (data) {
new Importer001().importData(data);
}
};

View File

@ -1,21 +1,17 @@
(function () {
"use strict";
var when = require('when');
var when = require('when');
module.exports = function (version, data) {
var importer;
module.exports = function (version, data) {
var importer;
try {
importer = require("./" + version);
} catch (ignore) {
// Zero effs given
}
try {
importer = require("./" + version);
} catch (ignore) {
// Zero effs given
}
if (!importer) {
return when.reject("No importer found");
}
if (!importer) {
return when.reject("No importer found");
}
return importer.importData(data);
};
}());
return importer.importData(data);
};

View File

@ -1,135 +1,128 @@
/*global require, exports */
var when = require('when'),
knex = require('../../models/base').Knex,
fixtures = require('../fixtures/001'),
up,
down;
(function () {
"use strict";
up = function () {
return when.all([
var when = require('when'),
knex = require('../../models/base').Knex,
fixtures = require('../fixtures/001'),
up,
down;
knex.Schema.createTable('posts', function (t) {
t.increments().primary();
t.string('uuid');
t.string('title');
t.string('slug');
t.text('content');
t.text('content_html');
t.string('meta_title');
t.string('meta_description');
t.string('meta_keywords');
t.bool('featured');
t.string('image');
t.string('status');
t.string('language');
t.integer('author_id');
t.dateTime('created_at');
t.integer('created_by');
t.dateTime('updated_at').nullable();
t.integer('updated_by').nullable();
t.dateTime('published_at').nullable();
t.integer('published_by').nullable();
}),
up = function () {
knex.Schema.createTable('users', function (t) {
t.increments().primary();
t.string('uuid');
t.string('full_name');
t.string('password');
t.string('email_address');
t.string('profile_picture');
t.string('cover_picture');
t.text('bio');
t.string('url');
t.dateTime('created_at');
t.integer('created_by');
t.dateTime('updated_at');
t.integer('updated_by');
}),
knex.Schema.createTable('roles', function (t) {
t.increments().primary();
t.string('name');
t.string('description');
}),
knex.Schema.createTable('roles_users', function (t) {
t.increments().primary();
t.integer('role_id');
t.integer('user_id');
}),
knex.Schema.createTable('permissions', function (t) {
t.increments().primary();
t.string('name');
t.string('object_type');
t.string('action_type');
t.integer('object_id');
}),
knex.Schema.createTable('permissions_users', function (t) {
t.increments().primary();
t.integer('user_id');
t.integer('permission_id');
}),
knex.Schema.createTable('permissions_roles', function (t) {
t.increments().primary();
t.integer('role_id');
t.integer('permission_id');
}),
knex.Schema.createTable('settings', function (t) {
t.increments().primary();
t.string('uuid');
t.string('key').unique();
t.text('value');
t.string('type');
t.dateTime('created_at');
t.integer('created_by');
t.dateTime('updated_at');
t.integer('updated_by');
})
// Once we create all of the initial tables, bootstrap any of the data
]).then(function () {
return when.all([
knex('posts').insert(fixtures.posts),
// knex('users').insert(fixtures.users),
knex('roles').insert(fixtures.roles),
// knex('roles_users').insert(fixtures.roles_users),
knex('permissions').insert(fixtures.permissions),
knex('permissions_roles').insert(fixtures.permissions_roles),
knex('settings').insert(fixtures.settings)
]);
knex.Schema.createTable('posts', function (t) {
t.increments().primary();
t.string('uuid');
t.string('title');
t.string('slug');
t.text('content');
t.text('content_html');
t.string('meta_title');
t.string('meta_description');
t.string('meta_keywords');
t.bool('featured');
t.string('image');
t.string('status');
t.string('language');
t.integer('author_id');
t.dateTime('created_at');
t.integer('created_by');
t.dateTime('updated_at').nullable();
t.integer('updated_by').nullable();
t.dateTime('published_at').nullable();
t.integer('published_by').nullable();
}),
});
};
knex.Schema.createTable('users', function (t) {
t.increments().primary();
t.string('uuid');
t.string('full_name');
t.string('password');
t.string('email_address');
t.string('profile_picture');
t.string('cover_picture');
t.text('bio');
t.string('url');
t.dateTime('created_at');
t.integer('created_by');
t.dateTime('updated_at');
t.integer('updated_by');
}),
knex.Schema.createTable('roles', function (t) {
t.increments().primary();
t.string('name');
t.string('description');
}),
knex.Schema.createTable('roles_users', function (t) {
t.increments().primary();
t.integer('role_id');
t.integer('user_id');
}),
knex.Schema.createTable('permissions', function (t) {
t.increments().primary();
t.string('name');
t.string('object_type');
t.string('action_type');
t.integer('object_id');
}),
knex.Schema.createTable('permissions_users', function (t) {
t.increments().primary();
t.integer('user_id');
t.integer('permission_id');
}),
knex.Schema.createTable('permissions_roles', function (t) {
t.increments().primary();
t.integer('role_id');
t.integer('permission_id');
}),
knex.Schema.createTable('settings', function (t) {
t.increments().primary();
t.string('uuid');
t.string('key').unique();
t.text('value');
t.string('type');
t.dateTime('created_at');
t.integer('created_by');
t.dateTime('updated_at');
t.integer('updated_by');
})
// Once we create all of the initial tables, bootstrap any of the data
]).then(function () {
return when.all([
knex('posts').insert(fixtures.posts),
// knex('users').insert(fixtures.users),
knex('roles').insert(fixtures.roles),
// knex('roles_users').insert(fixtures.roles_users),
knex('permissions').insert(fixtures.permissions),
knex('permissions_roles').insert(fixtures.permissions_roles),
knex('settings').insert(fixtures.settings)
]);
});
};
down = function () {
down = function () {
return when.all([
knex.Schema.dropTableIfExists("posts"),
knex.Schema.dropTableIfExists("users"),
knex.Schema.dropTableIfExists("roles"),
knex.Schema.dropTableIfExists("settings"),
knex.Schema.dropTableIfExists("permissions")
]).then(function () {
// Drop the relation tables after the model tables?
return when.all([
knex.Schema.dropTableIfExists("posts"),
knex.Schema.dropTableIfExists("users"),
knex.Schema.dropTableIfExists("roles"),
knex.Schema.dropTableIfExists("settings"),
knex.Schema.dropTableIfExists("permissions")
]).then(function () {
// Drop the relation tables after the model tables?
return when.all([
knex.Schema.dropTableIfExists("roles_users"),
knex.Schema.dropTableIfExists("permissions_users"),
knex.Schema.dropTableIfExists("permissions_roles")
]);
});
};
knex.Schema.dropTableIfExists("roles_users"),
knex.Schema.dropTableIfExists("permissions_users"),
knex.Schema.dropTableIfExists("permissions_roles")
]);
});
};
exports.up = up;
exports.down = down;
}());
exports.up = up;
exports.down = down;

View File

@ -1,60 +1,56 @@
(function () {
"use strict";
var _ = require('underscore'),
errors;
var _ = require('underscore'),
errors;
/**
* Basic error handling helpers
*/
errors = {
throwError: function (err) {
if (!err) {
err = new Error("An error occurred");
}
if (_.isString(err)) {
throw new Error(err);
}
throw err;
},
logError: function (err) {
err = err || "Unknown";
// TODO: Logging framework hookup
console.log("Error occurred: ", err.message || err);
},
logAndThrowError: function (err) {
this.logError(err);
this.throwError(err);
},
logErrorWithMessage: function (msg) {
var self = this;
return function () {
self.logError(msg);
};
},
logErrorWithRedirect: function (msg, redirectTo, req, res) {
var self = this;
return function () {
self.logError(msg);
if (_.isFunction(res.redirect)) {
res.redirect(redirectTo);
}
};
/**
* Basic error handling helpers
*/
errors = {
throwError: function (err) {
if (!err) {
err = new Error("An error occurred");
}
};
// Ensure our 'this' context in the functions
_.bindAll(errors, "throwError", "logError", "logAndThrowError", "logErrorWithMessage", "logErrorWithRedirect");
if (_.isString(err)) {
throw new Error(err);
}
module.exports = errors;
}());
throw err;
},
logError: function (err) {
err = err || "Unknown";
// TODO: Logging framework hookup
console.log("Error occurred: ", err.message || err);
},
logAndThrowError: function (err) {
this.logError(err);
this.throwError(err);
},
logErrorWithMessage: function (msg) {
var self = this;
return function () {
self.logError(msg);
};
},
logErrorWithRedirect: function (msg, redirectTo, req, res) {
var self = this;
return function () {
self.logError(msg);
if (_.isFunction(res.redirect)) {
res.redirect(redirectTo);
}
};
}
};
// Ensure our 'this' context in the functions
_.bindAll(errors, "throwError", "logError", "logAndThrowError", "logErrorWithMessage", "logErrorWithRedirect");
module.exports = errors;

View File

@ -1,96 +1,90 @@
(function () {
var GhostBookshelf,
Bookshelf = require('bookshelf'),
config = require('../../../config');
"use strict";
// Initializes Bookshelf as its own instance, so we can modify the Models and not mess up
// others' if they're using the library outside of ghost.
GhostBookshelf = Bookshelf.Initialize('ghost', config.database[process.env.NODE_ENV || 'development']);
var GhostBookshelf,
Bookshelf = require('bookshelf'),
config = require('../../../config');
// The Base Model which other Ghost objects will inherit from,
// including some convenience functions as static properties on the model.
GhostBookshelf.Model = GhostBookshelf.Model.extend({
// Initializes Bookshelf as its own instance, so we can modify the Models and not mess up
// others' if they're using the library outside of ghost.
GhostBookshelf = Bookshelf.Initialize('ghost', config.database[process.env.NODE_ENV || 'development']);
// Base prototype properties will go here
// The Base Model which other Ghost objects will inherit from,
// including some convenience functions as static properties on the model.
GhostBookshelf.Model = GhostBookshelf.Model.extend({
}, {
// Base prototype properties will go here
/**
* Naive find all
* @param options (optional)
*/
findAll: function (options) {
options = options || {};
return GhostBookshelf.Collection.forge([], {model: this}).fetch(options);
},
}, {
browse: function () {
return this.findAll.apply(this, arguments);
},
/**
* Naive find all
* @param options (optional)
*/
findAll: function (options) {
options = options || {};
return GhostBookshelf.Collection.forge([], {model: this}).fetch(options);
},
/**
* Naive find one where args match
* @param args
* @param options (optional)
*/
findOne: function (args, options) {
options = options || {};
return this.forge(args).fetch(options);
},
browse: function () {
return this.findAll.apply(this, arguments);
},
read: function () {
return this.findOne.apply(this, arguments);
},
/**
* Naive find one where args match
* @param args
* @param options (optional)
*/
findOne: function (args, options) {
options = options || {};
return this.forge(args).fetch(options);
},
/**
* Naive edit
* @param editedObj
* @param options (optional)
*/
edit: function (editedObj, options) {
options = options || {};
return this.forge({id: editedObj.id}).fetch(options).then(function (foundObj) {
return foundObj.set(editedObj).save();
});
},
read: function () {
return this.findOne.apply(this, arguments);
},
update: function () {
return this.edit.apply(this, arguments);
},
/**
* Naive edit
* @param editedObj
* @param options (optional)
*/
edit: function (editedObj, options) {
options = options || {};
return this.forge({id: editedObj.id}).fetch(options).then(function (foundObj) {
return foundObj.set(editedObj).save();
});
},
/**
* Naive create
* @param editedObj
* @param options (optional)
*/
add: function (newObj, options) {
options = options || {};
return this.forge(newObj).save(options);
},
update: function () {
return this.edit.apply(this, arguments);
},
create: function () {
return this.add.apply(this, arguments);
},
/**
* Naive create
* @param editedObj
* @param options (optional)
*/
add: function (newObj, options) {
options = options || {};
return this.forge(newObj).save(options);
},
/**
* Naive destroy
* @param _identifier
* @param options (optional)
*/
destroy: function (_identifier, options) {
options = options || {};
return this.forge({id: _identifier}).destroy(options);
},
create: function () {
return this.add.apply(this, arguments);
},
'delete': function () {
return this.destroy.apply(this, arguments);
}
/**
* Naive destroy
* @param _identifier
* @param options (optional)
*/
destroy: function (_identifier, options) {
options = options || {};
return this.forge({id: _identifier}).destroy(options);
},
});
'delete': function () {
return this.destroy.apply(this, arguments);
}
});
module.exports = GhostBookshelf;
}());
module.exports = GhostBookshelf;

View File

@ -1,29 +1,22 @@
/*global require, module */
var GhostBookshelf = require('./base'),
errors = require('../errorHandling'),
knex = GhostBookshelf.Knex;
(function () {
"use strict";
var GhostBookshelf = require('./base'),
errors = require('../errorHandling'),
knex = GhostBookshelf.Knex;
module.exports = {
Post: require('./post').Post,
User: require('./user').User,
Role: require('./role').Role,
Permission: require('./permission').Permission,
Settings: require('./settings').Settings,
init: function () {
return knex.Schema.hasTable('posts').then(null, function () {
// Simple bootstraping of the data model for now.
var migration = require('../data/migration/001');
return migration.down().then(function () {
return migration.up();
}, errors.logAndThrowError);
}, errors.logAndThrowError).then(function () {
console.log('models loaded');
module.exports = {
Post: require('./post').Post,
User: require('./user').User,
Role: require('./role').Role,
Permission: require('./permission').Permission,
Settings: require('./settings').Settings,
init: function () {
return knex.Schema.hasTable('posts').then(null, function () {
// Simple bootstraping of the data model for now.
var migration = require('../data/migration/001');
return migration.down().then(function () {
return migration.up();
}, errors.logAndThrowError);
}
};
}());
}, errors.logAndThrowError).then(function () {
console.log('models loaded');
}, errors.logAndThrowError);
}
};

View File

@ -1,31 +1,26 @@
(function () {
"use strict";
var GhostBookshelf = require('./base'),
User = require('./user').User,
Role = require('./role').Role,
Permission,
Permissions;
var GhostBookshelf = require('./base'),
User = require('./user').User,
Role = require('./role').Role,
Permission,
Permissions;
Permission = GhostBookshelf.Model.extend({
tableName: 'permissions',
Permission = GhostBookshelf.Model.extend({
tableName: 'permissions',
roles: function () {
return this.belongsToMany(Role);
},
roles: function () {
return this.belongsToMany(Role);
},
users: function () {
return this.belongsToMany(User);
}
});
users: function () {
return this.belongsToMany(User);
}
});
Permissions = GhostBookshelf.Collection.extend({
model: Permission
});
Permissions = GhostBookshelf.Collection.extend({
model: Permission
});
module.exports = {
Permission: Permission,
Permissions: Permissions
};
}());
module.exports = {
Permission: Permission,
Permissions: Permissions
};

View File

@ -1,87 +1,83 @@
(function () {
var Post,
Posts,
_ = require('underscore'),
uuid = require('node-uuid'),
when = require('when'),
errors = require('../errorHandling'),
Showdown = require('showdown'),
converter = new Showdown.converter(),
User = require('./user').User,
GhostBookshelf = require('./base');
"use strict";
Post = GhostBookshelf.Model.extend({
var Post,
Posts,
_ = require('underscore'),
uuid = require('node-uuid'),
when = require('when'),
errors = require('../errorHandling'),
Showdown = require('showdown'),
converter = new Showdown.converter(),
User = require('./user').User,
GhostBookshelf = require('./base');
tableName: 'posts',
Post = GhostBookshelf.Model.extend({
hasTimestamps: true,
tableName: 'posts',
defaults: function () {
return {
uuid: uuid.v4(),
status: 'draft'
// TODO: language: ghost.config().defaultLang);
};
},
hasTimestamps: true,
initialize: function () {
this.on('creating', this.creating, this);
this.on('saving', this.saving, this);
},
defaults: function () {
return {
uuid: uuid.v4(),
status: 'draft'
// TODO: language: ghost.config().defaultLang);
};
},
saving: function () {
if (!this.get('title')) {
throw new Error('Post title cannot be blank');
}
this.set('content_html', converter.makeHtml(this.get('content')));
initialize: function () {
this.on('creating', this.creating, this);
this.on('saving', this.saving, this);
},
saving: function () {
if (!this.get('title')) {
throw new Error('Post title cannot be blank');
}
this.set('content_html', converter.makeHtml(this.get('content')));
if (this.hasChanged('status') && this.get('status') === 'published') {
this.set('published_at', new Date());
// This will need to go elsewhere in the API layer.
this.set('published_by', 1);
}
this.set('updated_by', 1);
// refactoring of ghost required in order to make these details available here
},
creating: function () {
if (!this.get('slug')) {
this.generateSlug();
}
if (!this.get('created_by')) {
this.set('created_by', 1);
}
if (!this.get('author_id')) {
this.set('author_id', 1);
}
},
generateSlug: function () {
return this.set('slug', this.get('title').replace(/\:/g, '').replace(/\s/g, '-').toLowerCase());
},
user: function () {
return this.belongsTo(User, 'created_by');
},
author: function () {
return this.belongsTo(User, 'author_id');
if (this.hasChanged('status') && this.get('status') === 'published') {
this.set('published_at', new Date());
// This will need to go elsewhere in the API layer.
this.set('published_by', 1);
}
}, {
this.set('updated_by', 1);
// refactoring of ghost required in order to make these details available here
},
/**
* Find results by page - returns an object containing the
* information about the request (page, limit), along with the
* info needed for pagination (pages, total).
*
* {
creating: function () {
if (!this.get('slug')) {
this.generateSlug();
}
if (!this.get('created_by')) {
this.set('created_by', 1);
}
if (!this.get('author_id')) {
this.set('author_id', 1);
}
},
generateSlug: function () {
return this.set('slug', this.get('title').replace(/\:/g, '').replace(/\s/g, '-').toLowerCase());
},
user: function () {
return this.belongsTo(User, 'created_by');
},
author: function () {
return this.belongsTo(User, 'author_id');
}
}, {
/**
* Find results by page - returns an object containing the
* information about the request (page, limit), along with the
* info needed for pagination (pages, total).
*
* {
* posts: [
* {...}, {...}, {...}
* ],
@ -90,118 +86,116 @@
* pages: __,
* total: __
* }
*
* @params opts
*/
findPage: function (opts) {
var postCollection;
*
* @params opts
*/
findPage: function (opts) {
var postCollection;
// Allow findPage(n)
if (_.isString(opts) || _.isNumber(opts)) {
opts = {page: opts};
}
opts = _.extend({
page: 1,
limit: 15,
where: {},
status: 'published',
orderBy: ['published_at', 'DESC']
}, opts);
postCollection = Posts.forge();
// Unless `all` is passed as an option, filter on
// the status provided.
if (opts.status !== 'all') {
opts.where.status = opts.status;
}
// If there are where conditionals specified, add those
// to the query.
if (opts.where) {
postCollection.query('where', opts.where);
}
// Set the limit & offset for the query, fetching
// with the opts (to specify any eager relations, etc.)
// Omitting the `page`, `limit`, `where` just to be sure
// aren't used for other purposes.
return postCollection
.query('limit', opts.limit)
.query('offset', opts.limit * (opts.page - 1))
.query('orderBy', opts.orderBy[0], opts.orderBy[1])
.fetch(_.omit(opts, 'page', 'limit', 'where', 'status', 'orderBy'))
.then(function (collection) {
var qb;
// After we're done, we need to figure out what
// the limits are for the pagination values.
qb = GhostBookshelf.Knex(_.result(collection, 'tableName'));
if (opts.where) {
qb.where(opts.where);
}
return qb.count(_.result(collection, 'idAttribute')).then(function (resp) {
var totalPosts = resp[0].aggregate;
return {
posts: collection.toJSON(),
page: opts.page,
limit: opts.limit,
pages: Math.ceil(totalPosts / opts.limit),
total: totalPosts
};
}, errors.logAndThrowError);
}, errors.logAndThrowError);
},
permissable: function (postModelOrId, userId, action_type, userPermissions) {
var self = this,
hasPermission,
postModel = postModelOrId;
// If we passed in an id instead of a model, get the model
// then check the permissions
if (_.isNumber(postModelOrId) || _.isString(postModelOrId)) {
return this.read({id: postModelOrId}).then(function (foundPostModel) {
return self.permissable(foundPostModel, userId, action_type, userPermissions);
}, errors.logAndThrowError);
}
// TODO: This logic is temporary, will probably need to be updated
hasPermission = _.any(userPermissions, function (perm) {
if (perm.get('object_type') !== 'post') {
return false;
}
// True, if no object_id specified, or it matches
return !perm.get('object_id') || perm.get('object_id') === postModel.id;
});
// If this is the author of the post, allow it.
hasPermission = hasPermission || userId === postModel.get('author_id');
if (hasPermission) {
return when.resolve();
}
// Otherwise, you shall not pass.
return when.reject();
// Allow findPage(n)
if (_.isString(opts) || _.isNumber(opts)) {
opts = {page: opts};
}
});
opts = _.extend({
page: 1,
limit: 15,
where: {},
status: 'published',
orderBy: ['published_at', 'DESC']
}, opts);
Posts = GhostBookshelf.Collection.extend({
postCollection = Posts.forge();
model: Post
// Unless `all` is passed as an option, filter on
// the status provided.
if (opts.status !== 'all') {
opts.where.status = opts.status;
}
});
// If there are where conditionals specified, add those
// to the query.
if (opts.where) {
postCollection.query('where', opts.where);
}
module.exports = {
Post: Post,
Posts: Posts
};
// Set the limit & offset for the query, fetching
// with the opts (to specify any eager relations, etc.)
// Omitting the `page`, `limit`, `where` just to be sure
// aren't used for other purposes.
return postCollection
.query('limit', opts.limit)
.query('offset', opts.limit * (opts.page - 1))
.query('orderBy', opts.orderBy[0], opts.orderBy[1])
.fetch(_.omit(opts, 'page', 'limit', 'where', 'status', 'orderBy'))
.then(function (collection) {
var qb;
}());
// After we're done, we need to figure out what
// the limits are for the pagination values.
qb = GhostBookshelf.Knex(_.result(collection, 'tableName'));
if (opts.where) {
qb.where(opts.where);
}
return qb.count(_.result(collection, 'idAttribute')).then(function (resp) {
var totalPosts = resp[0].aggregate;
return {
posts: collection.toJSON(),
page: opts.page,
limit: opts.limit,
pages: Math.ceil(totalPosts / opts.limit),
total: totalPosts
};
}, errors.logAndThrowError);
}, errors.logAndThrowError);
},
permissable: function (postModelOrId, userId, action_type, userPermissions) {
var self = this,
hasPermission,
postModel = postModelOrId;
// If we passed in an id instead of a model, get the model
// then check the permissions
if (_.isNumber(postModelOrId) || _.isString(postModelOrId)) {
return this.read({id: postModelOrId}).then(function (foundPostModel) {
return self.permissable(foundPostModel, userId, action_type, userPermissions);
}, errors.logAndThrowError);
}
// TODO: This logic is temporary, will probably need to be updated
hasPermission = _.any(userPermissions, function (perm) {
if (perm.get('object_type') !== 'post') {
return false;
}
// True, if no object_id specified, or it matches
return !perm.get('object_id') || perm.get('object_id') === postModel.id;
});
// If this is the author of the post, allow it.
hasPermission = hasPermission || userId === postModel.get('author_id');
if (hasPermission) {
return when.resolve();
}
// Otherwise, you shall not pass.
return when.reject();
}
});
Posts = GhostBookshelf.Collection.extend({
model: Post
});
module.exports = {
Post: Post,
Posts: Posts
};

View File

@ -1,32 +1,26 @@
(function () {
var User = require('./user').User,
Permission = require('./permission').Permission,
GhostBookshelf = require('./base'),
Role,
Roles;
"use strict";
Role = GhostBookshelf.Model.extend({
tableName: 'roles',
var User = require('./user').User,
Permission = require('./permission').Permission,
GhostBookshelf = require('./base'),
Role,
Roles;
users: function () {
return this.belongsToMany(User);
},
Role = GhostBookshelf.Model.extend({
tableName: 'roles',
permissions: function () {
return this.belongsToMany(Permission);
}
});
users: function () {
return this.belongsToMany(User);
},
Roles = GhostBookshelf.Collection.extend({
model: Role
});
permissions: function () {
return this.belongsToMany(Permission);
}
});
Roles = GhostBookshelf.Collection.extend({
model: Role
});
module.exports = {
Role: Role,
Roles: Roles
};
}());
module.exports = {
Role: Role,
Roles: Roles
};

View File

@ -1,50 +1,45 @@
(function () {
"use strict";
var Settings,
GhostBookshelf = require('./base'),
uuid = require('node-uuid'),
_ = require('underscore'),
errors = require('../errorHandling'),
when = require('when');
var Settings,
GhostBookshelf = require('./base'),
uuid = require('node-uuid'),
_ = require('underscore'),
errors = require('../errorHandling'),
when = require('when');
// Each setting is saved as a separate row in the database,
// but the overlying API treats them as a single key:value mapping
Settings = GhostBookshelf.Model.extend({
tableName: 'settings',
hasTimestamps: true,
defaults: function () {
return {
uuid: uuid.v4(),
type: 'general'
};
// Each setting is saved as a separate row in the database,
// but the overlying API treats them as a single key:value mapping
Settings = GhostBookshelf.Model.extend({
tableName: 'settings',
hasTimestamps: true,
defaults: function () {
return {
uuid: uuid.v4(),
type: 'general'
};
}
}, {
read: function (_key) {
// Allow for just passing the key instead of attributes
if (!_.isObject(_key)) {
_key = { key: _key };
}
}, {
read: function (_key) {
// Allow for just passing the key instead of attributes
if (!_.isObject(_key)) {
_key = { key: _key };
}
return GhostBookshelf.Model.read.call(this, _key);
},
return GhostBookshelf.Model.read.call(this, _key);
},
edit: function (_data) {
var settings = this;
if (!Array.isArray(_data)) {
_data = [_data];
}
return when.map(_data, function (item) {
// Accept an array of models as input
if (item.toJSON) { item = item.toJSON(); }
return settings.forge({ key: item.key }).fetch().then(function (setting) {
return setting.set('value', item.value).save();
}, errors.logAndThrowError);
});
edit: function (_data) {
var settings = this;
if (!Array.isArray(_data)) {
_data = [_data];
}
});
return when.map(_data, function (item) {
// Accept an array of models as input
if (item.toJSON) { item = item.toJSON(); }
return settings.forge({ key: item.key }).fetch().then(function (setting) {
return setting.set('value', item.value).save();
}, errors.logAndThrowError);
});
}
});
module.exports = {
Settings: Settings
};
}());
module.exports = {
Settings: Settings
};

View File

@ -1,101 +1,98 @@
(function () {
"use strict";
var User,
Users,
UserRole,
// UserRoles,
_ = require('underscore'),
uuid = require('node-uuid'),
when = require('when'),
errors = require('../errorHandling'),
nodefn = require('when/node/function'),
bcrypt = require('bcrypt-nodejs'),
Posts = require('./post').Posts,
GhostBookshelf = require('./base'),
Role = require('./role').Role,
Permission = require('./permission').Permission;
var User,
Users,
UserRole,
// UserRoles,
_ = require('underscore'),
uuid = require('node-uuid'),
when = require('when'),
errors = require('../errorHandling'),
nodefn = require('when/node/function'),
bcrypt = require('bcrypt-nodejs'),
Posts = require('./post').Posts,
GhostBookshelf = require('./base'),
Role = require('./role').Role,
Permission = require('./permission').Permission;
UserRole = GhostBookshelf.Model.extend({
tableName: 'roles_users'
});
UserRole = GhostBookshelf.Model.extend({
tableName: 'roles_users'
});
User = GhostBookshelf.Model.extend({
User = GhostBookshelf.Model.extend({
tableName: 'users',
tableName: 'users',
hasTimestamps: true,
hasTimestamps: true,
defaults: function () {
return {
uuid: uuid.v4()
defaults: function () {
return {
uuid: uuid.v4()
};
},
posts: function () {
return this.hasMany(Posts, 'created_by');
},
roles: function () {
return this.belongsToMany(Role);
},
permissions: function () {
return this.belongsToMany(Permission);
}
}, {
/**
* Naive user add
* @param _user
*
* Hashes the password provided before saving to the database.
*/
add: function (_user) {
var User = this,
// Clone the _user so we don't expose the hashed password unnecessarily
userData = _.extend({}, _user),
fail = false,
userRoles = {
"role_id": 1,
"user_id": 1
};
},
posts: function () {
return this.hasMany(Posts, 'created_by');
},
roles: function () {
return this.belongsToMany(Role);
},
permissions: function () {
return this.belongsToMany(Permission);
}
}, {
/**
* Naive user add
* @param _user
*
* Hashes the password provided before saving to the database.
* This only allows one user to be added to the database, otherwise fails.
* @param {object} user
* @author javorszky
*/
add: function (_user) {
return this.forge().fetch().then(function (user) {
var User = this,
// Clone the _user so we don't expose the hashed password unnecessarily
userData = _.extend({}, _user),
fail = false,
userRoles = {
if (user) {
fail = true;
}
"role_id": 1,
"user_id": 1
};
if (fail) {
return when.reject(new Error('A user is already registered. Only one user for now!'));
}
/**
* This only allows one user to be added to the database, otherwise fails.
* @param {object} user
* @author javorszky
*/
return this.forge().fetch().then(function (user) {
if (user) {
fail = true;
}
if (fail) {
return when.reject(new Error('A user is already registered. Only one user for now!'));
}
return nodefn.call(bcrypt.hash, _user.password, null, null).then(function (hash) {
userData.password = hash;
GhostBookshelf.Model.add.call(UserRole, userRoles);
return GhostBookshelf.Model.add.call(User, userData);
}, errors.logAndThrowError);
return nodefn.call(bcrypt.hash, _user.password, null, null).then(function (hash) {
userData.password = hash;
GhostBookshelf.Model.add.call(UserRole, userRoles);
return GhostBookshelf.Model.add.call(User, userData);
}, errors.logAndThrowError);
}, errors.logAndThrowError);
/**
* Temporarily replacing the function below with another one that checks
* whether there's anyone registered at all. This is due to #138
* @author javorszky
*/
/**
return this.forge({email_address: userData.email_address}).fetch().then(function (user) {
/**
* Temporarily replacing the function below with another one that checks
* whether there's anyone registered at all. This is due to #138
* @author javorszky
*/
/**
return this.forge({email_address: userData.email_address}).fetch().then(function (user) {
if (!!user.attributes.email_address) {
return when.reject(new Error('A user with that email address already exists.'));
}
@ -105,66 +102,64 @@
return GhostBookshelf.Model.add.call(User, userData);
});
});
*/
},
/**
* User check
* @param _userdata
*
* Finds the user by email, and check's the password
*/
check: function (_userdata) {
return this.forge({
email_address: _userdata.email
}).fetch({require: true}).then(function (user) {
return nodefn.call(bcrypt.compare, _userdata.pw, user.get('password')).then(function (matched) {
if (!matched) {
return when.reject(new Error('Passwords do not match'));
}
return user;
}, errors.logAndThrowError);
},
/**
* User check
* @param _userdata
*
* Finds the user by email, and check's the password
*/
check: function (_userdata) {
return this.forge({
email_address: _userdata.email
}).fetch({require: true}).then(function (user) {
return nodefn.call(bcrypt.compare, _userdata.pw, user.get('password')).then(function (matched) {
if (!matched) {
return when.reject(new Error('Passwords do not match'));
}
return user;
}, errors.logAndThrowError);
},
}, errors.logAndThrowError);
},
effectivePermissions: function (id) {
return this.read({id: id}, { withRelated: ['permissions', 'roles.permissions'] })
.then(function (foundUser) {
var seenPerms = {},
rolePerms = _.map(foundUser.related('roles').models, function (role) {
return role.related('permissions').models;
}),
allPerms = [];
effectivePermissions: function (id) {
return this.read({id: id}, { withRelated: ['permissions', 'roles.permissions'] })
.then(function (foundUser) {
var seenPerms = {},
rolePerms = _.map(foundUser.related('roles').models, function (role) {
return role.related('permissions').models;
}),
allPerms = [];
rolePerms.push(foundUser.related('permissions').models);
rolePerms.push(foundUser.related('permissions').models);
_.each(rolePerms, function (rolePermGroup) {
_.each(rolePermGroup, function (perm) {
var key = perm.get('action_type') + '-' + perm.get('object_type') + '-' + perm.get('object_id');
_.each(rolePerms, function (rolePermGroup) {
_.each(rolePermGroup, function (perm) {
var key = perm.get('action_type') + '-' + perm.get('object_type') + '-' + perm.get('object_id');
// Only add perms once
if (seenPerms[key]) {
return;
}
// Only add perms once
if (seenPerms[key]) {
return;
}
allPerms.push(perm);
seenPerms[key] = true;
});
allPerms.push(perm);
seenPerms[key] = true;
});
});
return when.resolve(allPerms);
}, errors.logAndThrowError);
}
return when.resolve(allPerms);
}, errors.logAndThrowError);
}
});
});
Users = GhostBookshelf.Collection.extend({
model: User
});
Users = GhostBookshelf.Collection.extend({
model: User
});
module.exports = {
User: User,
Users: Users
};
}());
module.exports = {
User: User,
Users: Users
};

View File

@ -1,179 +1,174 @@
(function () {
"use strict";
// canThis(someUser).edit.posts([id]|[[ids]])
// canThis(someUser).edit.post(somePost|somePostId)
// canThis(someUser).edit.posts([id]|[[ids]])
// canThis(someUser).edit.post(somePost|somePostId)
var _ = require('underscore'),
when = require('when'),
Models = require('../models'),
objectTypeModelMap = require('./objectTypeModelMap'),
UserProvider = Models.User,
PermissionsProvider = Models.Permission,
init,
refresh,
canThis,
CanThisResult,
exported;
var _ = require('underscore'),
when = require('when'),
Models = require('../models'),
objectTypeModelMap = require('./objectTypeModelMap'),
UserProvider = Models.User,
PermissionsProvider = Models.Permission,
init,
refresh,
canThis,
CanThisResult,
exported;
// Base class for canThis call results
CanThisResult = function () {
this.userPermissionLoad = false;
};
// Base class for canThis call results
CanThisResult = function () {
this.userPermissionLoad = false;
};
CanThisResult.prototype.buildObjectTypeHandlers = function (obj_types, act_type, userId) {
var self = this,
obj_type_handlers = {};
CanThisResult.prototype.buildObjectTypeHandlers = function (obj_types, act_type, userId) {
var self = this,
obj_type_handlers = {};
// Iterate through the object types, i.e. ['post', 'tag', 'user']
_.each(obj_types, function (obj_type) {
var TargetModel = objectTypeModelMap[obj_type];
// Iterate through the object types, i.e. ['post', 'tag', 'user']
_.each(obj_types, function (obj_type) {
var TargetModel = objectTypeModelMap[obj_type];
// Create the 'handler' for the object type;
// the '.post()' in canThis(user).edit.post()
obj_type_handlers[obj_type] = function (modelOrId) {
var modelId;
// Create the 'handler' for the object type;
// the '.post()' in canThis(user).edit.post()
obj_type_handlers[obj_type] = function (modelOrId) {
var modelId;
if (_.isNumber(modelOrId) || _.isString(modelOrId)) {
// It's an id already, do nothing
modelId = modelOrId;
} else if (modelOrId) {
// It's a model, get the id
modelId = modelOrId.id;
}
// Wait for the user loading to finish
return self.userPermissionLoad.then(function (userPermissions) {
// Iterate through the user permissions looking for an affirmation
var hasPermission;
// Allow for a target model to implement a "Permissable" interface
if (TargetModel && _.isFunction(TargetModel.permissable)) {
return TargetModel.permissable(modelId, userId, act_type, userPermissions);
}
// Otherwise, check all the permissions for matching object id
hasPermission = _.any(userPermissions, function (userPermission) {
var permObjId;
// Look for a matching action type and object type first
if (userPermission.get('action_type') !== act_type || userPermission.get('object_type') !== obj_type) {
return false;
}
// Grab the object id (if specified, could be null)
permObjId = userPermission.get('object_id');
// If we didn't specify a model (any thing)
// or the permission didn't have an id scope set
// then the user has permission
if (!modelId || !permObjId) {
return true;
}
// Otherwise, check if the id's match
// TODO: String vs Int comparison possibility here?
return modelId === permObjId;
});
if (hasPermission) {
return when.resolve();
}
return when.reject();
}).otherwise(function () {
// No permissions loaded, or error loading permissions
// Still check for permissable without permissions
if (TargetModel && _.isFunction(TargetModel.permissable)) {
return TargetModel.permissable(modelId, userId, act_type, []);
}
return when.reject();
});
};
});
return obj_type_handlers;
};
CanThisResult.prototype.beginCheck = function (user) {
var self = this,
userId = user.id || user;
// TODO: Switch logic based on object type; user, role, post.
// Kick off the fetching of the user data
this.userPermissionLoad = UserProvider.effectivePermissions(userId);
// Iterate through the actions and their related object types
// We should have loaded these through a permissions.init() call previously
// TODO: Throw error if not init() yet?
_.each(exported.actionsMap, function (obj_types, act_type) {
// Build up the object type handlers;
// the '.post()' parts in canThis(user).edit.post()
var obj_type_handlers = self.buildObjectTypeHandlers(obj_types, act_type, userId);
// Define a property for the action on the result;
// the '.edit' in canThis(user).edit.post()
Object.defineProperty(self, act_type, {
writable: false,
enumerable: false,
configurable: false,
value: obj_type_handlers
});
});
// Return this for chaining
return this;
};
canThis = function (user) {
var result = new CanThisResult();
return result.beginCheck(user);
};
init = refresh = function () {
// Load all the permissions
return PermissionsProvider.browse().then(function (perms) {
var seenActions = {};
exported.actionsMap = {};
// Build a hash map of the actions on objects, i.e
/*
{
'edit': ['post', 'tag', 'user', 'page'],
'delete': ['post', 'user'],
'create': ['post', 'user', 'page']
if (_.isNumber(modelOrId) || _.isString(modelOrId)) {
// It's an id already, do nothing
modelId = modelOrId;
} else if (modelOrId) {
// It's a model, get the id
modelId = modelOrId.id;
}
*/
_.each(perms.models, function (perm) {
var action_type = perm.get('action_type'),
object_type = perm.get('object_type');
exported.actionsMap[action_type] = exported.actionsMap[action_type] || [];
seenActions[action_type] = seenActions[action_type] || {};
// Wait for the user loading to finish
return self.userPermissionLoad.then(function (userPermissions) {
// Iterate through the user permissions looking for an affirmation
var hasPermission;
// Check if we've already seen this action -> object combo
if (seenActions[action_type][object_type]) {
return;
// Allow for a target model to implement a "Permissable" interface
if (TargetModel && _.isFunction(TargetModel.permissable)) {
return TargetModel.permissable(modelId, userId, act_type, userPermissions);
}
exported.actionsMap[action_type].push(object_type);
seenActions[action_type][object_type] = true;
// Otherwise, check all the permissions for matching object id
hasPermission = _.any(userPermissions, function (userPermission) {
var permObjId;
// Look for a matching action type and object type first
if (userPermission.get('action_type') !== act_type || userPermission.get('object_type') !== obj_type) {
return false;
}
// Grab the object id (if specified, could be null)
permObjId = userPermission.get('object_id');
// If we didn't specify a model (any thing)
// or the permission didn't have an id scope set
// then the user has permission
if (!modelId || !permObjId) {
return true;
}
// Otherwise, check if the id's match
// TODO: String vs Int comparison possibility here?
return modelId === permObjId;
});
if (hasPermission) {
return when.resolve();
}
return when.reject();
}).otherwise(function () {
// No permissions loaded, or error loading permissions
// Still check for permissable without permissions
if (TargetModel && _.isFunction(TargetModel.permissable)) {
return TargetModel.permissable(modelId, userId, act_type, []);
}
return when.reject();
});
};
});
return when(exported.actionsMap);
return obj_type_handlers;
};
CanThisResult.prototype.beginCheck = function (user) {
var self = this,
userId = user.id || user;
// TODO: Switch logic based on object type; user, role, post.
// Kick off the fetching of the user data
this.userPermissionLoad = UserProvider.effectivePermissions(userId);
// Iterate through the actions and their related object types
// We should have loaded these through a permissions.init() call previously
// TODO: Throw error if not init() yet?
_.each(exported.actionsMap, function (obj_types, act_type) {
// Build up the object type handlers;
// the '.post()' parts in canThis(user).edit.post()
var obj_type_handlers = self.buildObjectTypeHandlers(obj_types, act_type, userId);
// Define a property for the action on the result;
// the '.edit' in canThis(user).edit.post()
Object.defineProperty(self, act_type, {
writable: false,
enumerable: false,
configurable: false,
value: obj_type_handlers
});
};
});
module.exports = exported = {
init: init,
refresh: refresh,
canThis: canThis,
actionsMap: {}
};
// Return this for chaining
return this;
};
}());
canThis = function (user) {
var result = new CanThisResult();
return result.beginCheck(user);
};
init = refresh = function () {
// Load all the permissions
return PermissionsProvider.browse().then(function (perms) {
var seenActions = {};
exported.actionsMap = {};
// Build a hash map of the actions on objects, i.e
/*
{
'edit': ['post', 'tag', 'user', 'page'],
'delete': ['post', 'user'],
'create': ['post', 'user', 'page']
}
*/
_.each(perms.models, function (perm) {
var action_type = perm.get('action_type'),
object_type = perm.get('object_type');
exported.actionsMap[action_type] = exported.actionsMap[action_type] || [];
seenActions[action_type] = seenActions[action_type] || {};
// Check if we've already seen this action -> object combo
if (seenActions[action_type][object_type]) {
return;
}
exported.actionsMap[action_type].push(object_type);
seenActions[action_type][object_type] = true;
});
return when(exported.actionsMap);
});
};
module.exports = exported = {
init: init,
refresh: refresh,
canThis: canThis,
actionsMap: {}
};

View File

@ -1,11 +1,7 @@
(function () {
"use strict";
module.exports = {
'post': require('../models/post').Post,
'role': require('../models/role').Role,
'user': require('../models/user').User,
'permission': require('../models/permission').Permission,
'setting': require('../models/settings').Settings
};
}());
module.exports = {
'post': require('../models/post').Post,
'role': require('../models/role').Role,
'user': require('../models/user').User,
'permission': require('../models/permission').Permission,
'setting': require('../models/settings').Settings
};

View File

@ -1,70 +1,67 @@
(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];
}
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;
}
return obj;
},
readDir = function (dir, options, depth) {
depth = depth || 0;
options = extend({
index: true
}, options);
options = extend({
index: true
}, options);
if (depth > 1) {
return null;
if (depth > 1) {
return null;
}
var subtree = {},
treeDeferred = when.defer(),
treePromise = treeDeferred.promise;
fs.readdir(dir, function (error, files) {
if (error) {
return treeDeferred.reject(error);
}
var subtree = {},
treeDeferred = when.defer(),
treePromise = treeDeferred.promise;
files = files || [];
fs.readdir(dir, function (error, files) {
if (error) {
return treeDeferred.reject(error);
}
files = 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);
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 when(treePromise).then(function (prom) {
return prom;
return keys.all(subtree).then(function(theFiles) {
return treeDeferred.resolve(theFiles);
});
},
readAll = function (dir, options, depth) {
return when(readDir(dir, options, depth)).then(function (paths) {
return paths;
});
};
});
module.exports = readAll;
}());
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;

View File

@ -1,171 +1,165 @@
/*globals describe, beforeEach, it*/
/*globals describe, it, beforeEach */
var should = require('should'),
helpers = require('./helpers'),
errors = require('../../shared/errorHandling'),
Models = require('../../shared/models');
(function () {
"use strict";
describe("Role Model", function () {
var should = require('should'),
helpers = require('./helpers'),
errors = require('../../shared/errorHandling'),
Models = require('../../shared/models');
var RoleModel = Models.Role;
describe("Role Model", function () {
should.exist(RoleModel);
var RoleModel = Models.Role;
should.exist(RoleModel);
beforeEach(function (done) {
helpers.resetData().then(function () {
done();
}, done);
});
it("can browse roles", function (done) {
RoleModel.browse().then(function (foundRoles) {
should.exist(foundRoles);
foundRoles.models.length.should.be.above(0);
done();
}, errors.logError);
});
it("can read roles", function (done) {
RoleModel.read({id: 1}).then(function (foundRole) {
should.exist(foundRole);
done();
}, errors.logError);
});
it("can edit roles", function (done) {
RoleModel.read({id: 1}).then(function (foundRole) {
should.exist(foundRole);
return foundRole.set({name: "updated"}).save();
}).then(function () {
return RoleModel.read({id: 1});
}).then(function (updatedRole) {
should.exist(updatedRole);
updatedRole.get("name").should.equal("updated");
done();
}, errors.logError);
});
it("can add roles", function (done) {
var newRole = {
name: "test1",
description: "test1 description"
};
RoleModel.add(newRole).then(function (createdRole) {
should.exist(createdRole);
createdRole.attributes.name.should.equal(newRole.name);
createdRole.attributes.description.should.equal(newRole.description);
done();
}, done);
});
it("can delete roles", function (done) {
RoleModel.read({id: 1}).then(function (foundRole) {
should.exist(foundRole);
return RoleModel['delete'](1);
}).then(function () {
return RoleModel.browse();
}).then(function (foundRoles) {
var hasRemovedId = foundRoles.any(function (role) {
return role.id === 1;
});
hasRemovedId.should.equal(false);
done();
}, errors.logError);
});
beforeEach(function (done) {
helpers.resetData().then(function () {
done();
}, done);
});
describe("Permission Model", function () {
it("can browse roles", function (done) {
RoleModel.browse().then(function (foundRoles) {
should.exist(foundRoles);
var PermissionModel = Models.Permission;
foundRoles.models.length.should.be.above(0);
should.exist(PermissionModel);
beforeEach(function (done) {
helpers.resetData().then(function () {
done();
}, done);
});
it("can browse permissions", function (done) {
PermissionModel.browse().then(function (foundPermissions) {
should.exist(foundPermissions);
foundPermissions.models.length.should.be.above(0);
done();
}, errors.logError);
});
it("can read permissions", function (done) {
PermissionModel.read({id: 1}).then(function (foundPermission) {
should.exist(foundPermission);
done();
}, errors.logError);
});
it("can edit permissions", function (done) {
PermissionModel.read({id: 1}).then(function (foundPermission) {
should.exist(foundPermission);
return foundPermission.set({name: "updated"}).save();
}).then(function () {
return PermissionModel.read({id: 1});
}).then(function (updatedPermission) {
should.exist(updatedPermission);
updatedPermission.get("name").should.equal("updated");
done();
}, errors.logError);
});
it("can add permissions", function (done) {
var newPerm = {
name: "testperm1"
};
PermissionModel.add(newPerm).then(function (createdPerm) {
should.exist(createdPerm);
createdPerm.attributes.name.should.equal(newPerm.name);
done();
}, done);
});
it("can delete permissions", function (done) {
PermissionModel.read({id: 1}).then(function (foundPermission) {
should.exist(foundPermission);
return PermissionModel['delete'](1);
}).then(function () {
return PermissionModel.browse();
}).then(function (foundPermissions) {
var hasRemovedId = foundPermissions.any(function (permission) {
return permission.id === 1;
});
hasRemovedId.should.equal(false);
done();
}, errors.logError);
});
done();
}, errors.logError);
});
}());
it("can read roles", function (done) {
RoleModel.read({id: 1}).then(function (foundRole) {
should.exist(foundRole);
done();
}, errors.logError);
});
it("can edit roles", function (done) {
RoleModel.read({id: 1}).then(function (foundRole) {
should.exist(foundRole);
return foundRole.set({name: "updated"}).save();
}).then(function () {
return RoleModel.read({id: 1});
}).then(function (updatedRole) {
should.exist(updatedRole);
updatedRole.get("name").should.equal("updated");
done();
}, errors.logError);
});
it("can add roles", function (done) {
var newRole = {
name: "test1",
description: "test1 description"
};
RoleModel.add(newRole).then(function (createdRole) {
should.exist(createdRole);
createdRole.attributes.name.should.equal(newRole.name);
createdRole.attributes.description.should.equal(newRole.description);
done();
}, done);
});
it("can delete roles", function (done) {
RoleModel.read({id: 1}).then(function (foundRole) {
should.exist(foundRole);
return RoleModel['delete'](1);
}).then(function () {
return RoleModel.browse();
}).then(function (foundRoles) {
var hasRemovedId = foundRoles.any(function (role) {
return role.id === 1;
});
hasRemovedId.should.equal(false);
done();
}, errors.logError);
});
});
describe("Permission Model", function () {
var PermissionModel = Models.Permission;
should.exist(PermissionModel);
beforeEach(function (done) {
helpers.resetData().then(function () {
done();
}, done);
});
it("can browse permissions", function (done) {
PermissionModel.browse().then(function (foundPermissions) {
should.exist(foundPermissions);
foundPermissions.models.length.should.be.above(0);
done();
}, errors.logError);
});
it("can read permissions", function (done) {
PermissionModel.read({id: 1}).then(function (foundPermission) {
should.exist(foundPermission);
done();
}, errors.logError);
});
it("can edit permissions", function (done) {
PermissionModel.read({id: 1}).then(function (foundPermission) {
should.exist(foundPermission);
return foundPermission.set({name: "updated"}).save();
}).then(function () {
return PermissionModel.read({id: 1});
}).then(function (updatedPermission) {
should.exist(updatedPermission);
updatedPermission.get("name").should.equal("updated");
done();
}, errors.logError);
});
it("can add permissions", function (done) {
var newPerm = {
name: "testperm1"
};
PermissionModel.add(newPerm).then(function (createdPerm) {
should.exist(createdPerm);
createdPerm.attributes.name.should.equal(newPerm.name);
done();
}, done);
});
it("can delete permissions", function (done) {
PermissionModel.read({id: 1}).then(function (foundPermission) {
should.exist(foundPermission);
return PermissionModel['delete'](1);
}).then(function () {
return PermissionModel.browse();
}).then(function (foundPermissions) {
var hasRemovedId = foundPermissions.any(function (permission) {
return permission.id === 1;
});
hasRemovedId.should.equal(false);
done();
}, errors.logError);
});
});

View File

@ -1,172 +1,167 @@
/*globals describe, beforeEach, it*/
/*globals describe, beforeEach, it */
var _ = require("underscore"),
should = require('should'),
helpers = require('./helpers'),
Models = require('../../shared/models');
(function () {
"use strict";
describe('Post Model', function () {
var _ = require("underscore"),
should = require('should'),
helpers = require('./helpers'),
Models = require('../../shared/models');
var PostModel = Models.Post;
describe('Post Model', function () {
var PostModel = Models.Post;
beforeEach(function (done) {
helpers.resetData().then(function () {
done();
}, done);
});
it('can browse', function (done) {
PostModel.browse().then(function (results) {
should.exist(results);
results.length.should.equal(2);
// should be in published_at, DESC order
results.models[0].attributes.published_at.should.be.above(results.models[1].attributes.published_at);
done();
}).then(null, done);
});
it('can read', function (done) {
var firstPost;
PostModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
firstPost = results.models[0];
return PostModel.read({slug: firstPost.attributes.slug});
}).then(function (found) {
should.exist(found);
found.attributes.title.should.equal(firstPost.attributes.title);
done();
}).then(null, done);
});
it('can edit', function (done) {
var firstPost;
PostModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
firstPost = results.models[0];
return PostModel.edit({id: firstPost.id, title: "new title"});
}).then(function (edited) {
should.exist(edited);
edited.attributes.title.should.equal('new title');
done();
}).then(null, done);
});
it('can add, defaulting as a draft', function (done) {
var createdPostUpdatedDate,
newPost = {
title: 'Test Title 1',
content: 'Test Content 1'
};
PostModel.add(newPost).then(function (createdPost) {
return new PostModel({id: createdPost.id}).fetch();
}).then(function (createdPost) {
should.exist(createdPost);
createdPost.has('uuid').should.equal(true);
createdPost.get('status').should.equal('draft');
createdPost.get('title').should.equal(newPost.title, "title is correct");
createdPost.get('content').should.equal(newPost.content, "content is correct");
createdPost.get('slug').should.equal(newPost.title.toLowerCase().replace(/ /g, '-'), 'slug is correct');
//createdPost.get('created_at').should.be.instanceOf(Date); - why is this not true?
createdPost.get('created_by').should.equal(1);
createdPost.get('author_id').should.equal(1);
createdPost.get('created_by').should.equal(createdPost.get('author_id'));
//createdPost.get('updated_at').should.be.instanceOf(Date); - why is this not true?
createdPost.get('updated_by').should.equal(1);
should.equal(createdPost.get('published_at'), null);
should.equal(createdPost.get('published_by'), null);
createdPostUpdatedDate = createdPost.get('updated_at');
// Set the status to published to check that `published_at` is set.
return createdPost.save({status: 'published'});
}).then(function (publishedPost) {
publishedPost.get('published_at').should.be.instanceOf(Date);
publishedPost.get('published_by').should.equal(1);
publishedPost.get('updated_at').should.be.instanceOf(Date);
publishedPost.get('updated_by').should.equal(1);
publishedPost.get('updated_at').should.not.equal(createdPostUpdatedDate);
done();
}).then(null, done);
});
it('can delete', function (done) {
var firstPostId;
PostModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
firstPostId = results.models[0].id;
return PostModel.destroy(firstPostId);
}).then(function () {
return PostModel.browse();
}).then(function (newResults) {
var ids, hasDeletedId;
ids = _.pluck(newResults.models, "id");
hasDeletedId = _.any(ids, function (id) {
return id === firstPostId;
});
hasDeletedId.should.equal(false);
done();
}).then(null, done);
});
it('can fetch a paginated set, with various options', function (done) {
this.timeout(4000);
helpers.insertMorePosts().then(function () {
return PostModel.findPage({page: 2});
}).then(function (paginationResult) {
paginationResult.page.should.equal(2);
paginationResult.limit.should.equal(15);
paginationResult.posts.length.should.equal(15);
paginationResult.pages.should.equal(4);
return PostModel.findPage({page: 5});
}).then(function (paginationResult) {
paginationResult.page.should.equal(5);
paginationResult.limit.should.equal(15);
paginationResult.posts.length.should.equal(0);
paginationResult.pages.should.equal(4);
return PostModel.findPage({limit: 30});
}).then(function (paginationResult) {
paginationResult.page.should.equal(1);
paginationResult.limit.should.equal(30);
paginationResult.posts.length.should.equal(30);
paginationResult.pages.should.equal(2);
return PostModel.findPage({limit: 10, page: 2, where: {language: 'fr'}});
}).then(function (paginationResult) {
paginationResult.page.should.equal(2);
paginationResult.limit.should.equal(10);
paginationResult.posts.length.should.equal(10);
paginationResult.pages.should.equal(3);
return PostModel.findPage({limit: 10, page: 2, status: 'all'});
}).then(function (paginationResult) {
paginationResult.pages.should.equal(11);
done();
}).then(null, done);
});
beforeEach(function (done) {
helpers.resetData().then(function () {
done();
}, done);
});
}());
it('can browse', function (done) {
PostModel.browse().then(function (results) {
should.exist(results);
results.length.should.equal(2);
// should be in published_at, DESC order
results.models[0].attributes.published_at.should.be.above(results.models[1].attributes.published_at);
done();
}).then(null, done);
});
it('can read', function (done) {
var firstPost;
PostModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
firstPost = results.models[0];
return PostModel.read({slug: firstPost.attributes.slug});
}).then(function (found) {
should.exist(found);
found.attributes.title.should.equal(firstPost.attributes.title);
done();
}).then(null, done);
});
it('can edit', function (done) {
var firstPost;
PostModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
firstPost = results.models[0];
return PostModel.edit({id: firstPost.id, title: "new title"});
}).then(function (edited) {
should.exist(edited);
edited.attributes.title.should.equal('new title');
done();
}).then(null, done);
});
it('can add, defaulting as a draft', function (done) {
var createdPostUpdatedDate,
newPost = {
title: 'Test Title 1',
content: 'Test Content 1'
};
PostModel.add(newPost).then(function (createdPost) {
return new PostModel({id: createdPost.id}).fetch();
}).then(function (createdPost) {
should.exist(createdPost);
createdPost.has('uuid').should.equal(true);
createdPost.get('status').should.equal('draft');
createdPost.get('title').should.equal(newPost.title, "title is correct");
createdPost.get('content').should.equal(newPost.content, "content is correct");
createdPost.get('slug').should.equal(newPost.title.toLowerCase().replace(/ /g, '-'), 'slug is correct');
//createdPost.get('created_at').should.be.instanceOf(Date); - why is this not true?
createdPost.get('created_by').should.equal(1);
createdPost.get('author_id').should.equal(1);
createdPost.get('created_by').should.equal(createdPost.get('author_id'));
//createdPost.get('updated_at').should.be.instanceOf(Date); - why is this not true?
createdPost.get('updated_by').should.equal(1);
should.equal(createdPost.get('published_at'), null);
should.equal(createdPost.get('published_by'), null);
createdPostUpdatedDate = createdPost.get('updated_at');
// Set the status to published to check that `published_at` is set.
return createdPost.save({status: 'published'});
}).then(function (publishedPost) {
publishedPost.get('published_at').should.be.instanceOf(Date);
publishedPost.get('published_by').should.equal(1);
publishedPost.get('updated_at').should.be.instanceOf(Date);
publishedPost.get('updated_by').should.equal(1);
publishedPost.get('updated_at').should.not.equal(createdPostUpdatedDate);
done();
}).then(null, done);
});
it('can delete', function (done) {
var firstPostId;
PostModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
firstPostId = results.models[0].id;
return PostModel.destroy(firstPostId);
}).then(function () {
return PostModel.browse();
}).then(function (newResults) {
var ids, hasDeletedId;
ids = _.pluck(newResults.models, "id");
hasDeletedId = _.any(ids, function (id) {
return id === firstPostId;
});
hasDeletedId.should.equal(false);
done();
}).then(null, done);
});
it('can fetch a paginated set, with various options', function (done) {
this.timeout(4000);
helpers.insertMorePosts().then(function () {
return PostModel.findPage({page: 2});
}).then(function (paginationResult) {
paginationResult.page.should.equal(2);
paginationResult.limit.should.equal(15);
paginationResult.posts.length.should.equal(15);
paginationResult.pages.should.equal(4);
return PostModel.findPage({page: 5});
}).then(function (paginationResult) {
paginationResult.page.should.equal(5);
paginationResult.limit.should.equal(15);
paginationResult.posts.length.should.equal(0);
paginationResult.pages.should.equal(4);
return PostModel.findPage({limit: 30});
}).then(function (paginationResult) {
paginationResult.page.should.equal(1);
paginationResult.limit.should.equal(30);
paginationResult.posts.length.should.equal(30);
paginationResult.pages.should.equal(2);
return PostModel.findPage({limit: 10, page: 2, where: {language: 'fr'}});
}).then(function (paginationResult) {
paginationResult.page.should.equal(2);
paginationResult.limit.should.equal(10);
paginationResult.posts.length.should.equal(10);
paginationResult.pages.should.equal(3);
return PostModel.findPage({limit: 10, page: 2, status: 'all'});
}).then(function (paginationResult) {
paginationResult.pages.should.equal(11);
done();
}).then(null, done);
});
});

View File

@ -1,183 +1,178 @@
/*globals describe, beforeEach, it*/
var _ = require("underscore"),
should = require('should'),
helpers = require('./helpers'),
Models = require('../../shared/models');
(function () {
"use strict";
describe('Settings Model', function () {
var _ = require("underscore"),
should = require('should'),
helpers = require('./helpers'),
Models = require('../../shared/models');
var SettingsModel = Models.Settings;
describe('Settings Model', function () {
var SettingsModel = Models.Settings;
beforeEach(function (done) {
helpers.resetData().then(function () {
done();
}, done);
});
it('can browse', function (done) {
SettingsModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
done();
}).then(null, done);
});
it('can read', function (done) {
var firstSetting;
SettingsModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
firstSetting = results.models[0];
return SettingsModel.read(firstSetting.attributes.key);
}).then(function (found) {
should.exist(found);
found.attributes.value.should.equal(firstSetting.attributes.value);
done();
}).then(null, done);
});
it('can edit single', function (done) {
var firstSetting;
SettingsModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
firstSetting = results.models[0];
// The edit method has been modified to take an object of
// key/value pairs
firstSetting.set('value', 'new value');
return SettingsModel.edit(firstSetting);
}).then(function (edited) {
should.exist(edited);
edited.length.should.equal(1);
edited = edited[0];
edited.attributes.key.should.equal(firstSetting.attributes.key);
edited.attributes.value.should.equal('new value');
done();
}).then(null, done);
});
it('can edit multiple', function (done) {
var model1,
model2,
editedModel;
SettingsModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
model1 = results.models[0];
model2 = results.models[1];
// The edit method has been modified to take an object of
// key/value pairs
model1.set('value', 'new value1');
model2.set('value', 'new value2');
return SettingsModel.edit([model1, model2]);
}).then(function (edited) {
should.exist(edited);
edited.length.should.equal(2);
editedModel = edited[0];
editedModel.attributes.key.should.equal(model1.attributes.key);
editedModel.attributes.value.should.equal('new value1');
editedModel = edited[1];
editedModel.attributes.key.should.equal(model2.attributes.key);
editedModel.attributes.value.should.equal('new value2');
done();
}).then(null, done);
});
it('can add', function (done) {
var newSetting = {
key: 'TestSetting1',
value: 'Test Content 1'
};
SettingsModel.add(newSetting).then(function (createdSetting) {
should.exist(createdSetting);
createdSetting.has('uuid').should.equal(true);
createdSetting.attributes.key.should.equal(newSetting.key, "key is correct");
createdSetting.attributes.value.should.equal(newSetting.value, "value is correct");
createdSetting.attributes.type.should.equal("general");
done();
}).then(null, done);
});
it('can delete', function (done) {
var firstSettingId;
SettingsModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
firstSettingId = results.models[0].id;
return SettingsModel.destroy(firstSettingId);
}).then(function () {
return SettingsModel.browse();
}).then(function (newResults) {
var ids, hasDeletedId;
ids = _.pluck(newResults.models, "id");
hasDeletedId = _.any(ids, function (id) {
return id === firstSettingId;
});
hasDeletedId.should.equal(false);
done();
}).then(null, done);
});
beforeEach(function (done) {
helpers.resetData().then(function () {
done();
}, done);
});
}());
it('can browse', function (done) {
SettingsModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
done();
}).then(null, done);
});
it('can read', function (done) {
var firstSetting;
SettingsModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
firstSetting = results.models[0];
return SettingsModel.read(firstSetting.attributes.key);
}).then(function (found) {
should.exist(found);
found.attributes.value.should.equal(firstSetting.attributes.value);
done();
}).then(null, done);
});
it('can edit single', function (done) {
var firstSetting;
SettingsModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
firstSetting = results.models[0];
// The edit method has been modified to take an object of
// key/value pairs
firstSetting.set('value', 'new value');
return SettingsModel.edit(firstSetting);
}).then(function (edited) {
should.exist(edited);
edited.length.should.equal(1);
edited = edited[0];
edited.attributes.key.should.equal(firstSetting.attributes.key);
edited.attributes.value.should.equal('new value');
done();
}).then(null, done);
});
it('can edit multiple', function (done) {
var model1,
model2,
editedModel;
SettingsModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
model1 = results.models[0];
model2 = results.models[1];
// The edit method has been modified to take an object of
// key/value pairs
model1.set('value', 'new value1');
model2.set('value', 'new value2');
return SettingsModel.edit([model1, model2]);
}).then(function (edited) {
should.exist(edited);
edited.length.should.equal(2);
editedModel = edited[0];
editedModel.attributes.key.should.equal(model1.attributes.key);
editedModel.attributes.value.should.equal('new value1');
editedModel = edited[1];
editedModel.attributes.key.should.equal(model2.attributes.key);
editedModel.attributes.value.should.equal('new value2');
done();
}).then(null, done);
});
it('can add', function (done) {
var newSetting = {
key: 'TestSetting1',
value: 'Test Content 1'
};
SettingsModel.add(newSetting).then(function (createdSetting) {
should.exist(createdSetting);
createdSetting.has('uuid').should.equal(true);
createdSetting.attributes.key.should.equal(newSetting.key, "key is correct");
createdSetting.attributes.value.should.equal(newSetting.value, "value is correct");
createdSetting.attributes.type.should.equal("general");
done();
}).then(null, done);
});
it('can delete', function (done) {
var firstSettingId;
SettingsModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
firstSettingId = results.models[0].id;
return SettingsModel.destroy(firstSettingId);
}).then(function () {
return SettingsModel.browse();
}).then(function (newResults) {
var ids, hasDeletedId;
ids = _.pluck(newResults.models, "id");
hasDeletedId = _.any(ids, function (id) {
return id === firstSettingId;
});
hasDeletedId.should.equal(false);
done();
}).then(null, done);
});
});

View File

@ -1,163 +1,158 @@
/*globals describe, beforeEach, it*/
var _ = require('underscore'),
should = require('should'),
helpers = require('./helpers'),
errors = require('../../shared/errorHandling'),
Models = require('../../shared/models'),
when = require('when');
(function () {
"use strict";
require('mocha-as-promised')();
var _ = require('underscore'),
should = require('should'),
helpers = require('./helpers'),
errors = require('../../shared/errorHandling'),
Models = require('../../shared/models'),
when = require('when');
describe('User Model', function () {
require('mocha-as-promised')();
var UserModel = Models.User;
describe('User Model', function () {
var UserModel = Models.User;
beforeEach(function (done) {
helpers.resetData().then(function (result) {
return when(helpers.insertDefaultUser());
}).then(function (results) {
done();
});
});
it('can add first', function (done) {
var userData = {
password: 'testpass1',
email_address: "test@test1.com"
};
when(helpers.resetData()).then(function (result) {
UserModel.add(userData).then(function (createdUser) {
should.exist(createdUser);
createdUser.has('uuid').should.equal(true);
createdUser.attributes.password.should.not.equal(userData.password, "password was hashed");
createdUser.attributes.email_address.should.eql(userData.email_address, "email address corred");
done();
}, done);
});
});
it('can\'t add second', function (done) {
var userData = {
password: 'testpass3',
email_address: "test3@test1.com"
};
return when(UserModel.add(userData)).otherwise(function (failure) {
return failure.message.should.eql('A user is already registered. Only one user for now!');
});
});
it('can browse', function (done) {
UserModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
done();
}).then(null, done);
});
it('can read', function (done) {
var firstUser;
UserModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
firstUser = results.models[0];
return UserModel.read({email_address: firstUser.attributes.email_address});
}).then(function (found) {
should.exist(found);
found.attributes.full_name.should.equal(firstUser.attributes.full_name);
done();
}).then(null, done);
});
it('can edit', function (done) {
var firstUser;
UserModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
firstUser = results.models[0];
return UserModel.edit({id: firstUser.id, url: "some.newurl.com"});
}).then(function (edited) {
should.exist(edited);
edited.attributes.url.should.equal('some.newurl.com');
done();
}).then(null, done);
});
it("can get effective permissions", function (done) {
UserModel.effectivePermissions(1).then(function (effectivePermissions) {
should.exist(effectivePermissions);
effectivePermissions.length.should.be.above(0);
done();
}, errors.logError);
});
it('can delete', function (done) {
var firstUserId;
UserModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
firstUserId = results.models[0].id;
return UserModel.destroy(firstUserId);
}).then(function () {
return UserModel.browse();
}).then(function (newResults) {
var ids, hasDeletedId;
if (newResults.length < 1) {
// Bug out if we only had one user and deleted it.
return done();
}
ids = _.pluck(newResults.models, "id");
hasDeletedId = _.any(ids, function (id) {
return id === firstUserId;
});
hasDeletedId.should.equal(false);
done();
}).then(null, done);
beforeEach(function (done) {
helpers.resetData().then(function (result) {
return when(helpers.insertDefaultUser());
}).then(function (results) {
done();
});
});
}());
it('can add first', function (done) {
var userData = {
password: 'testpass1',
email_address: "test@test1.com"
};
when(helpers.resetData()).then(function (result) {
UserModel.add(userData).then(function (createdUser) {
should.exist(createdUser);
createdUser.has('uuid').should.equal(true);
createdUser.attributes.password.should.not.equal(userData.password, "password was hashed");
createdUser.attributes.email_address.should.eql(userData.email_address, "email address corred");
done();
}, done);
});
});
it('can\'t add second', function (done) {
var userData = {
password: 'testpass3',
email_address: "test3@test1.com"
};
return when(UserModel.add(userData)).otherwise(function (failure) {
return failure.message.should.eql('A user is already registered. Only one user for now!');
});
});
it('can browse', function (done) {
UserModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
done();
}).then(null, done);
});
it('can read', function (done) {
var firstUser;
UserModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
firstUser = results.models[0];
return UserModel.read({email_address: firstUser.attributes.email_address});
}).then(function (found) {
should.exist(found);
found.attributes.full_name.should.equal(firstUser.attributes.full_name);
done();
}).then(null, done);
});
it('can edit', function (done) {
var firstUser;
UserModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
firstUser = results.models[0];
return UserModel.edit({id: firstUser.id, url: "some.newurl.com"});
}).then(function (edited) {
should.exist(edited);
edited.attributes.url.should.equal('some.newurl.com');
done();
}).then(null, done);
});
it("can get effective permissions", function (done) {
UserModel.effectivePermissions(1).then(function (effectivePermissions) {
should.exist(effectivePermissions);
effectivePermissions.length.should.be.above(0);
done();
}, errors.logError);
});
it('can delete', function (done) {
var firstUserId;
UserModel.browse().then(function (results) {
should.exist(results);
results.length.should.be.above(0);
firstUserId = results.models[0].id;
return UserModel.destroy(firstUserId);
}).then(function () {
return UserModel.browse();
}).then(function (newResults) {
var ids, hasDeletedId;
if (newResults.length < 1) {
// Bug out if we only had one user and deleted it.
return done();
}
ids = _.pluck(newResults.models, "id");
hasDeletedId = _.any(ids, function (id) {
return id === firstUserId;
});
hasDeletedId.should.equal(false);
done();
}).then(null, done);
});
});

View File

@ -1,111 +1,106 @@
/*globals describe, beforeEach, it*/
var should = require('should'),
when = require('when'),
sinon = require('sinon'),
errors = require('../../shared/errorHandling');
(function () {
"use strict";
describe("Error handling", function () {
var should = require('should'),
when = require('when'),
sinon = require('sinon'),
errors = require('../../shared/errorHandling');
// Just getting rid of jslint unused error
should.exist(errors);
describe("Error handling", function () {
it("throws error objects", function () {
var toThrow = new Error("test1"),
runThrowError = function () {
errors.throwError(toThrow);
};
// Just getting rid of jslint unused error
should.exist(errors);
it("throws error objects", function () {
var toThrow = new Error("test1"),
runThrowError = function () {
errors.throwError(toThrow);
};
runThrowError.should['throw']("test1");
});
it("throws error strings", function () {
var toThrow = "test2",
runThrowError = function () {
errors.throwError(toThrow);
};
runThrowError.should['throw']("test2");
});
it("throws error even if nothing passed", function () {
var runThrowError = function () {
errors.throwError();
};
runThrowError.should['throw']("An error occurred");
});
it("logs errors", function () {
var err = new Error("test1"),
logStub = sinon.stub(console, "log");
errors.logError(err);
// Calls log with message on Error objects
logStub.calledWith("Error occurred: ", err.message).should.equal(true);
logStub.reset();
err = "test2";
errors.logError(err);
// Calls log with string on strings
logStub.calledWith("Error occurred: ", err).should.equal(true);
logStub.restore();
});
it("logs promise errors with custom messages", function (done) {
var def = when.defer(),
prom = def.promise,
logStub = sinon.stub(console, "log");
prom.then(function () {
throw new Error("Ran success handler");
}, errors.logErrorWithMessage("test1"));
prom.otherwise(function () {
logStub.calledWith("Error occurred: ", "test1").should.equal(true);
logStub.restore();
done();
});
def.reject();
});
it("logs promise errors and redirects", function (done) {
var def = when.defer(),
prom = def.promise,
req = null,
res = {
redirect: function () {
return;
}
},
logStub = sinon.stub(console, "log"),
redirectStub = sinon.stub(res, "redirect");
prom.then(function () {
throw new Error("Ran success handler");
}, errors.logErrorWithRedirect("test1", "/testurl", req, res));
prom.otherwise(function () {
logStub.calledWith("Error occurred: ", "test1").should.equal(true);
logStub.restore();
redirectStub.calledWith('/testurl').should.equal(true);
redirectStub.restore();
done();
});
def.reject();
});
runThrowError.should['throw']("test1");
});
}());
it("throws error strings", function () {
var toThrow = "test2",
runThrowError = function () {
errors.throwError(toThrow);
};
runThrowError.should['throw']("test2");
});
it("throws error even if nothing passed", function () {
var runThrowError = function () {
errors.throwError();
};
runThrowError.should['throw']("An error occurred");
});
it("logs errors", function () {
var err = new Error("test1"),
logStub = sinon.stub(console, "log");
errors.logError(err);
// Calls log with message on Error objects
logStub.calledWith("Error occurred: ", err.message).should.equal(true);
logStub.reset();
err = "test2";
errors.logError(err);
// Calls log with string on strings
logStub.calledWith("Error occurred: ", err).should.equal(true);
logStub.restore();
});
it("logs promise errors with custom messages", function (done) {
var def = when.defer(),
prom = def.promise,
logStub = sinon.stub(console, "log");
prom.then(function () {
throw new Error("Ran success handler");
}, errors.logErrorWithMessage("test1"));
prom.otherwise(function () {
logStub.calledWith("Error occurred: ", "test1").should.equal(true);
logStub.restore();
done();
});
def.reject();
});
it("logs promise errors and redirects", function (done) {
var def = when.defer(),
prom = def.promise,
req = null,
res = {
redirect: function () {
return;
}
},
logStub = sinon.stub(console, "log"),
redirectStub = sinon.stub(res, "redirect");
prom.then(function () {
throw new Error("Ran success handler");
}, errors.logErrorWithRedirect("test1", "/testurl", req, res));
prom.otherwise(function () {
logStub.calledWith("Error occurred: ", "test1").should.equal(true);
logStub.restore();
redirectStub.calledWith('/testurl').should.equal(true);
redirectStub.restore();
done();
});
def.reject();
});
});

View File

@ -1,67 +1,60 @@
/*globals describe, beforeEach, it*/
var _ = require("underscore"),
should = require('should'),
when = require('when'),
sinon = require('sinon'),
helpers = require('./helpers'),
exporter = require('../../shared/data/export'),
Exporter001 = require('../../shared/data/export/001'),
errors = require('../../shared/errorHandling');
(function () {
"use strict";
describe("Export", function () {
var _ = require("underscore"),
should = require('should'),
when = require('when'),
sinon = require('sinon'),
helpers = require('./helpers'),
exporter = require('../../shared/data/export'),
Exporter001 = require('../../shared/data/export/001'),
errors = require('../../shared/errorHandling');
describe("Export", function () {
should.exist(exporter);
beforeEach(function (done) {
helpers.resetData().then(function () {
done();
}, done);
});
it("resolves 001", function (done) {
var exportStub = sinon.stub(Exporter001, "exportData", function () {
return when.resolve();
});
exporter("001").then(function () {
exportStub.called.should.equal(true);
exportStub.restore();
done();
}, errors.throwError);
});
describe("001", function () {
should.exist(Exporter001);
it("exports data", function (done) {
exporter("001").then(function (exportData) {
var tables = ['posts', 'users', 'roles', 'roles_users', 'permissions', 'permissions_roles', 'settings'];
should.exist(exportData);
should.exist(exportData.meta);
should.exist(exportData.data);
exportData.meta.version.should.equal("001");
_.each(tables, function (name) {
should.exist(exportData.data[name]);
});
done();
}, function () {
console.log("Error in exporter");
});
});
});
should.exist(exporter);
beforeEach(function (done) {
helpers.resetData().then(function () {
done();
}, done);
});
}());
it("resolves 001", function (done) {
var exportStub = sinon.stub(Exporter001, "exportData", function () {
return when.resolve();
});
exporter("001").then(function () {
exportStub.called.should.equal(true);
exportStub.restore();
done();
}, errors.throwError);
});
describe("001", function () {
should.exist(Exporter001);
it("exports data", function (done) {
exporter("001").then(function (exportData) {
var tables = ['posts', 'users', 'roles', 'roles_users', 'permissions', 'permissions_roles', 'settings'];
should.exist(exportData);
should.exist(exportData.meta);
should.exist(exportData.data);
exportData.meta.version.should.equal("001");
_.each(tables, function (name) {
should.exist(exportData.data[name]);
});
done();
}, function () {
console.log("Error in exporter");
});
});
});
});

View File

@ -1,69 +1,65 @@
/*globals describe, beforeEach, it*/
(function () {
"use strict";
var should = require('should'),
sinon = require('sinon'),
_ = require('underscore'),
path = require('path'),
GhostNavHelper = require('../../frontend/helpers/ghostNav');
var should = require('should'),
sinon = require('sinon'),
_ = require('underscore'),
path = require('path'),
GhostNavHelper = require('../../frontend/helpers/ghostNav');
describe('ghostNav Helper', function () {
var navTemplatePath = path.join(process.cwd(), 'core/frontend/views/nav.hbs');
describe('ghostNav Helper', function () {
var navTemplatePath = path.join(process.cwd(), 'core/frontend/views/nav.hbs');
should.exist(GhostNavHelper, "GhostNavHelper exists");
should.exist(GhostNavHelper, "GhostNavHelper exists");
it('can compile the nav template', function (done) {
var helper = new GhostNavHelper(navTemplatePath);
it('can compile the nav template', function (done) {
var helper = new GhostNavHelper(navTemplatePath);
helper.compileTemplate().then(function () {
should.exist(helper.navTemplateFunc);
_.isFunction(helper.navTemplateFunc).should.equal(true);
helper.compileTemplate().then(function () {
should.exist(helper.navTemplateFunc);
_.isFunction(helper.navTemplateFunc).should.equal(true);
done();
}, done);
});
it('can render nav items', function () {
var helper = new GhostNavHelper(function (data) { return "rendered " + data.links.length; }),
templateSpy = sinon.spy(helper, 'navTemplateFunc'),
fakeNavItems = [{
title: 'test1',
url: '/test1'
}, {
title: 'test2',
url: '/test2'
}],
rendered;
rendered = helper.renderNavItems(fakeNavItems);
// Returns a string returned from navTemplateFunc
should.exist(rendered);
rendered.should.equal("rendered 2");
templateSpy.calledWith({ links: fakeNavItems }).should.equal(true);
});
it('can register with ghost', function (done) {
var fakeGhost = {
paths: function () {
return {
frontendViews: path.join(process.cwd(), 'core/frontend/views/')
};
},
registerThemeHelper: function () {
return;
}
},
registerStub = sinon.stub(fakeGhost, 'registerThemeHelper');
GhostNavHelper.registerWithGhost(fakeGhost).then(function () {
registerStub.called.should.equal(true);
done();
}, done);
});
done();
}, done);
});
}());
it('can render nav items', function () {
var helper = new GhostNavHelper(function (data) { return "rendered " + data.links.length; }),
templateSpy = sinon.spy(helper, 'navTemplateFunc'),
fakeNavItems = [{
title: 'test1',
url: '/test1'
}, {
title: 'test2',
url: '/test2'
}],
rendered;
rendered = helper.renderNavItems(fakeNavItems);
// Returns a string returned from navTemplateFunc
should.exist(rendered);
rendered.should.equal("rendered 2");
templateSpy.calledWith({ links: fakeNavItems }).should.equal(true);
});
it('can register with ghost', function (done) {
var fakeGhost = {
paths: function () {
return {
frontendViews: path.join(process.cwd(), 'core/frontend/views/')
};
},
registerThemeHelper: function () {
return;
}
},
registerStub = sinon.stub(fakeGhost, 'registerThemeHelper');
GhostNavHelper.registerWithGhost(fakeGhost).then(function () {
registerStub.called.should.equal(true);
done();
}, done);
});
});

View File

@ -1,101 +1,94 @@
/*globals describe, beforeEach, it*/
var should = require('should'),
when = require('when'),
sinon = require('sinon'),
Ghost = require('../../ghost');
(function () {
"use strict";
describe("Ghost API", function () {
var should = require('should'),
when = require('when'),
sinon = require('sinon'),
Ghost = require('../../ghost');
it("is a singleton", function () {
var logStub = sinon.stub(console, "log"),
ghost1 = new Ghost(),
ghost2 = new Ghost();
describe("Ghost API", function () {
should.strictEqual(ghost1, ghost2);
logStub.restore();
});
it("is a singleton", function () {
var logStub = sinon.stub(console, "log"),
ghost1 = new Ghost(),
ghost2 = new Ghost();
it("uses init() to initialize", function (done) {
var ghost = new Ghost(),
fakeDataProvider = {
init: function () {
return when.resolve();
}
},
dataProviderInitSpy = sinon.spy(fakeDataProvider, "init"),
oldDataProvider = ghost.dataProvider;
should.strictEqual(ghost1, ghost2);
logStub.restore();
});
ghost.dataProvider = fakeDataProvider;
it("uses init() to initialize", function (done) {
var ghost = new Ghost(),
fakeDataProvider = {
init: function () {
return when.resolve();
}
},
dataProviderInitSpy = sinon.spy(fakeDataProvider, "init"),
oldDataProvider = ghost.dataProvider;
should.not.exist(ghost.settings());
ghost.dataProvider = fakeDataProvider;
ghost.init().then(function () {
should.not.exist(ghost.settings());
should.exist(ghost.settings());
ghost.init().then(function () {
dataProviderInitSpy.called.should.equal(true);
should.exist(ghost.settings());
ghost.dataProvider = oldDataProvider;
dataProviderInitSpy.called.should.equal(true);
ghost.dataProvider = oldDataProvider;
done();
}).then(null, done);
});
it("can register filters with specific priority", function () {
var ghost = new Ghost(),
filterName = 'test',
filterPriority = 9,
testFilterHandler = sinon.spy();
ghost.registerFilter(filterName, filterPriority, testFilterHandler);
should.exist(ghost.filterCallbacks[filterName]);
should.exist(ghost.filterCallbacks[filterName][filterPriority]);
ghost.filterCallbacks[filterName][filterPriority].should.include(testFilterHandler);
});
it("can register filters with default priority", function () {
var ghost = new Ghost(),
filterName = 'test',
defaultPriority = 5,
testFilterHandler = sinon.spy();
ghost.registerFilter(filterName, testFilterHandler);
should.exist(ghost.filterCallbacks[filterName]);
should.exist(ghost.filterCallbacks[filterName][defaultPriority]);
ghost.filterCallbacks[filterName][defaultPriority].should.include(testFilterHandler);
});
it("executes filters in priority order", function (done) {
var ghost = new Ghost(),
filterName = 'testpriority',
testFilterHandler1 = sinon.spy(),
testFilterHandler2 = sinon.spy(),
testFilterHandler3 = sinon.spy();
ghost.registerFilter(filterName, 0, testFilterHandler1);
ghost.registerFilter(filterName, 2, testFilterHandler2);
ghost.registerFilter(filterName, 9, testFilterHandler3);
ghost.doFilter(filterName, null, function () {
testFilterHandler1.calledBefore(testFilterHandler2).should.equal(true);
testFilterHandler2.calledBefore(testFilterHandler3).should.equal(true);
testFilterHandler3.called.should.equal(true);
done();
});
});
done();
}).then(null, done);
});
}());
it("can register filters with specific priority", function () {
var ghost = new Ghost(),
filterName = 'test',
filterPriority = 9,
testFilterHandler = sinon.spy();
ghost.registerFilter(filterName, filterPriority, testFilterHandler);
should.exist(ghost.filterCallbacks[filterName]);
should.exist(ghost.filterCallbacks[filterName][filterPriority]);
ghost.filterCallbacks[filterName][filterPriority].should.include(testFilterHandler);
});
it("can register filters with default priority", function () {
var ghost = new Ghost(),
filterName = 'test',
defaultPriority = 5,
testFilterHandler = sinon.spy();
ghost.registerFilter(filterName, testFilterHandler);
should.exist(ghost.filterCallbacks[filterName]);
should.exist(ghost.filterCallbacks[filterName][defaultPriority]);
ghost.filterCallbacks[filterName][defaultPriority].should.include(testFilterHandler);
});
it("executes filters in priority order", function (done) {
var ghost = new Ghost(),
filterName = 'testpriority',
testFilterHandler1 = sinon.spy(),
testFilterHandler2 = sinon.spy(),
testFilterHandler3 = sinon.spy();
ghost.registerFilter(filterName, 0, testFilterHandler1);
ghost.registerFilter(filterName, 2, testFilterHandler2);
ghost.registerFilter(filterName, 9, testFilterHandler3);
ghost.doFilter(filterName, null, function () {
testFilterHandler1.calledBefore(testFilterHandler2).should.equal(true);
testFilterHandler2.calledBefore(testFilterHandler3).should.equal(true);
testFilterHandler3.called.should.equal(true);
done();
});
});
});

File diff suppressed because one or more lines are too long

View File

@ -1,85 +1,78 @@
/*globals describe, beforeEach, it*/
var _ = require("underscore"),
should = require('should'),
when = require('when'),
sinon = require('sinon'),
knex = require("../../shared/models/base").Knex,
helpers = require('./helpers'),
exporter = require('../../shared/data/export'),
importer = require('../../shared/data/import'),
Importer001 = require('../../shared/data/import/001'),
errors = require('../../shared/errorHandling');
(function () {
"use strict";
describe("Import", function () {
var _ = require("underscore"),
should = require('should'),
when = require('when'),
sinon = require('sinon'),
knex = require("../../shared/models/base").Knex,
helpers = require('./helpers'),
exporter = require('../../shared/data/export'),
importer = require('../../shared/data/import'),
Importer001 = require('../../shared/data/import/001'),
errors = require('../../shared/errorHandling');
describe("Import", function () {
should.exist(exporter);
beforeEach(function (done) {
helpers.resetData().then(function () {
done();
}, done);
});
it("resolves 001", function (done) {
var importStub = sinon.stub(Importer001, "importData", function () {
return when.resolve();
}),
fakeData = { test: true };
importer("001", fakeData).then(function () {
importStub.calledWith(fakeData).should.equal(true);
importStub.restore();
done();
}, errors.throwError);
});
describe("001", function () {
this.timeout(4000);
should.exist(Importer001);
it("imports data from 001", function (done) {
var exportData;
// TODO: Should have static test data here?
exporter("001").then(function (exported) {
exportData = exported;
// Clear the data from all tables.
var tables = ['posts', 'users', 'roles', 'roles_users', 'permissions', 'permissions_roles', 'settings'],
truncateOps = _.map(tables, function (name) {
return knex(name).truncate();
});
return when.all(truncateOps);
}).then(function () {
return importer("001", exportData);
}).then(function (importResult) {
// Grab the data from tables
return when.all([
knex("users").select(),
knex("posts").select(),
knex("settings").select()
]);
}).then(function (importedData) {
should.exist(importedData);
importedData.length.should.equal(3);
importedData[0].length.should.equal(exportData.data.users.length);
importedData[1].length.should.equal(exportData.data.posts.length);
importedData[2].length.should.equal(exportData.data.settings.length);
done();
});
});
});
should.exist(exporter);
beforeEach(function (done) {
helpers.resetData().then(function () {
done();
}, done);
});
}());
it("resolves 001", function (done) {
var importStub = sinon.stub(Importer001, "importData", function () {
return when.resolve();
}),
fakeData = { test: true };
importer("001", fakeData).then(function () {
importStub.calledWith(fakeData).should.equal(true);
importStub.restore();
done();
}, errors.throwError);
});
describe("001", function () {
this.timeout(4000);
should.exist(Importer001);
it("imports data from 001", function (done) {
var exportData;
// TODO: Should have static test data here?
exporter("001").then(function (exported) {
exportData = exported;
// Clear the data from all tables.
var tables = ['posts', 'users', 'roles', 'roles_users', 'permissions', 'permissions_roles', 'settings'],
truncateOps = _.map(tables, function (name) {
return knex(name).truncate();
});
return when.all(truncateOps);
}).then(function () {
return importer("001", exportData);
}).then(function (importResult) {
// Grab the data from tables
return when.all([
knex("users").select(),
knex("posts").select(),
knex("settings").select()
]);
}).then(function (importedData) {
should.exist(importedData);
importedData.length.should.equal(3);
importedData[0].length.should.equal(exportData.data.users.length);
importedData[1].length.should.equal(exportData.data.posts.length);
importedData[2].length.should.equal(exportData.data.settings.length);
done();
});
});
});
});

View File

@ -1,311 +1,304 @@
/*globals describe, beforeEach, it*/
var _ = require("underscore"),
when = require('when'),
should = require('should'),
sinon = require('sinon'),
errors = require('../../shared/errorHandling'),
helpers = require('./helpers'),
permissions = require('../../shared/permissions'),
Models = require('../../shared/models'),
UserProvider = Models.User,
PermissionsProvider = Models.Permission,
PostProvider = Models.Post;
(function () {
"use strict";
describe('permissions', function () {
var _ = require("underscore"),
when = require('when'),
should = require('should'),
sinon = require('sinon'),
errors = require('../../shared/errorHandling'),
helpers = require('./helpers'),
permissions = require('../../shared/permissions'),
Models = require('../../shared/models'),
UserProvider = Models.User,
PermissionsProvider = Models.Permission,
PostProvider = Models.Post;
should.exist(permissions);
describe('permissions', function () {
should.exist(permissions);
beforeEach(function (done) {
helpers.resetData().then(function (result) {
return when(helpers.insertDefaultUser());
}).then(function (results) {
done();
});
beforeEach(function (done) {
helpers.resetData().then(function (result) {
return when(helpers.insertDefaultUser());
}).then(function (results) {
done();
});
});
// beforeEach(function (done) {
// helpers.resetData().then(function () { done(); }, errors.throwError);
// });
// beforeEach(function (done) {
// helpers.resetData().then(function () { done(); }, errors.throwError);
// });
var testPerms = [
{ act: "edit", obj: "post" },
{ act: "edit", obj: "tag" },
{ act: "edit", obj: "user" },
{ act: "edit", obj: "page" },
{ act: "add", obj: "post" },
{ act: "add", obj: "user" },
{ act: "add", obj: "page" },
{ act: "remove", obj: "post" },
{ act: "remove", obj: "user" }
],
currTestPermId = 1,
// currTestUserId = 1,
// createTestUser = function (email_address) {
// if (!email_address) {
// currTestUserId += 1;
// email_address = "test" + currTestPermId + "@test.com";
// }
var testPerms = [
{ act: "edit", obj: "post" },
{ act: "edit", obj: "tag" },
{ act: "edit", obj: "user" },
{ act: "edit", obj: "page" },
{ act: "add", obj: "post" },
{ act: "add", obj: "user" },
{ act: "add", obj: "page" },
{ act: "remove", obj: "post" },
{ act: "remove", obj: "user" }
],
currTestPermId = 1,
// currTestUserId = 1,
// createTestUser = function (email_address) {
// if (!email_address) {
// currTestUserId += 1;
// email_address = "test" + currTestPermId + "@test.com";
// }
// var newUser = {
// id: currTestUserId,
// email_address: email_address,
// password: "testing123"
// };
// var newUser = {
// id: currTestUserId,
// email_address: email_address,
// password: "testing123"
// };
// return UserProvider.add(newUser);
// },
createPermission = function (name, act, obj) {
if (!name) {
currTestPermId += 1;
name = "test" + currTestPermId;
}
// return UserProvider.add(newUser);
// },
createPermission = function (name, act, obj) {
if (!name) {
currTestPermId += 1;
name = "test" + currTestPermId;
}
var newPerm = {
name: name,
action_type: act,
object_type: obj
};
return PermissionsProvider.add(newPerm);
},
createTestPermissions = function () {
var createActions = _.map(testPerms, function (testPerm) {
return createPermission(null, testPerm.act, testPerm.obj);
});
return when.all(createActions);
var newPerm = {
name: name,
action_type: act,
object_type: obj
};
it('can load an actions map from existing permissions', function (done) {
return PermissionsProvider.add(newPerm);
},
createTestPermissions = function () {
var createActions = _.map(testPerms, function (testPerm) {
return createPermission(null, testPerm.act, testPerm.obj);
});
createTestPermissions()
.then(permissions.init)
.then(function (actionsMap) {
should.exist(actionsMap);
return when.all(createActions);
};
actionsMap.edit.should.eql(['post', 'tag', 'user', 'page']);
it('can load an actions map from existing permissions', function (done) {
actionsMap.should.equal(permissions.actionsMap);
createTestPermissions()
.then(permissions.init)
.then(function (actionsMap) {
should.exist(actionsMap);
done();
}, errors.throwError);
});
actionsMap.edit.should.eql(['post', 'tag', 'user', 'page']);
it('can add user to role', function (done) {
var existingUserRoles;
UserProvider.read({id: 1}, { withRelated: ['roles'] }).then(function (foundUser) {
var testRole = new Models.Role({
name: 'testrole1',
description: 'testrole1 description'
});
should.exist(foundUser);
should.exist(foundUser.roles());
existingUserRoles = foundUser.related('roles').length;
return testRole.save().then(function () {
return foundUser.roles().attach(testRole);
});
}).then(function () {
return UserProvider.read({id: 1}, { withRelated: ['roles'] });
}).then(function (updatedUser) {
should.exist(updatedUser);
updatedUser.related('roles').length.should.equal(existingUserRoles + 1);
actionsMap.should.equal(permissions.actionsMap);
done();
}, errors.throwError);
});
it('can add user to role', function (done) {
var existingUserRoles;
UserProvider.read({id: 1}, { withRelated: ['roles'] }).then(function (foundUser) {
var testRole = new Models.Role({
name: 'testrole1',
description: 'testrole1 description'
});
should.exist(foundUser);
should.exist(foundUser.roles());
existingUserRoles = foundUser.related('roles').length;
return testRole.save().then(function () {
return foundUser.roles().attach(testRole);
});
}).then(function () {
return UserProvider.read({id: 1}, { withRelated: ['roles'] });
}).then(function (updatedUser) {
should.exist(updatedUser);
updatedUser.related('roles').length.should.equal(existingUserRoles + 1);
done();
});
});
it('can add user permissions', function (done) {
Models.User.read({id: 1}, { withRelated: ['permissions']}).then(function (testUser) {
var testPermission = new Models.Permission({
name: "test edit posts",
action_type: 'edit',
object_type: 'post'
});
testUser.related('permissions').length.should.equal(0);
return testPermission.save().then(function () {
return testUser.permissions().attach(testPermission);
});
}).then(function () {
return Models.User.read({id: 1}, { withRelated: ['permissions']});
}).then(function (updatedUser) {
should.exist(updatedUser);
updatedUser.related('permissions').length.should.equal(1);
done();
});
});
it('can add role permissions', function (done) {
var testRole = new Models.Role({
name: "test2",
description: "test2 description"
});
it('can add user permissions', function (done) {
Models.User.read({id: 1}, { withRelated: ['permissions']}).then(function (testUser) {
var testPermission = new Models.Permission({
testRole.save()
.then(function () {
return testRole.load('permissions');
})
.then(function () {
var rolePermission = new Models.Permission({
name: "test edit posts",
action_type: 'edit',
object_type: 'post'
});
testUser.related('permissions').length.should.equal(0);
testRole.related('permissions').length.should.equal(0);
return testPermission.save().then(function () {
return testUser.permissions().attach(testPermission);
return rolePermission.save().then(function () {
return testRole.permissions().attach(rolePermission);
});
}).then(function () {
return Models.User.read({id: 1}, { withRelated: ['permissions']});
}).then(function (updatedUser) {
should.exist(updatedUser);
})
.then(function () {
return Models.Role.read({id: testRole.id}, { withRelated: ['permissions']});
})
.then(function (updatedRole) {
should.exist(updatedRole);
updatedUser.related('permissions').length.should.equal(1);
updatedRole.related('permissions').length.should.equal(1);
done();
});
});
it('can add role permissions', function (done) {
var testRole = new Models.Role({
name: "test2",
description: "test2 description"
});
testRole.save()
.then(function () {
return testRole.load('permissions');
})
.then(function () {
var rolePermission = new Models.Permission({
name: "test edit posts",
action_type: 'edit',
object_type: 'post'
});
testRole.related('permissions').length.should.equal(0);
return rolePermission.save().then(function () {
return testRole.permissions().attach(rolePermission);
});
})
.then(function () {
return Models.Role.read({id: testRole.id}, { withRelated: ['permissions']});
})
.then(function (updatedRole) {
should.exist(updatedRole);
updatedRole.related('permissions').length.should.equal(1);
done();
});
});
it('does not allow edit post without permission', function (done) {
var fakePage = {
id: 1
};
createTestPermissions()
.then(permissions.init)
.then(function () {
return Models.User.read({id: 1});
})
.then(function (foundUser) {
var canThisResult = permissions.canThis(foundUser);
should.exist(canThisResult.edit);
should.exist(canThisResult.edit.post);
return canThisResult.edit.page(fakePage);
})
.then(function () {
errors.logError(new Error("Allowed edit post without permission"));
}, function () {
done();
});
});
it('allows edit post with permission', function (done) {
var fakePost = {
id: "1"
};
createTestPermissions()
.then(permissions.init)
.then(function () {
return Models.User.read({id: 1});
})
.then(function (foundUser) {
var newPerm = new Models.Permission({
name: "test3 edit post",
action_type: "edit",
object_type: "post"
});
return newPerm.save().then(function () {
return foundUser.permissions().attach(newPerm);
});
})
.then(function () {
return Models.User.read({id: 1}, { withRelated: ['permissions']});
})
.then(function (updatedUser) {
// TODO: Verify updatedUser.related('permissions') has the permission?
var canThisResult = permissions.canThis(updatedUser);
should.exist(canThisResult.edit);
should.exist(canThisResult.edit.post);
return canThisResult.edit.post(fakePost);
})
.then(function () {
done();
}, function () {
errors.logError(new Error("Did not allow edit post with permission"));
});
});
it('can use permissable function on Model to allow something', function (done) {
var testUser,
permissableStub = sinon.stub(PostProvider, 'permissable', function () {
return when.resolve();
});
// createTestUser()
UserProvider.browse()
.then(function (foundUser) {
testUser = foundUser.models[0];
return permissions.canThis(testUser).edit.post(123);
})
.then(function () {
permissableStub.restore();
permissableStub.calledWith(123, testUser.id, 'edit').should.equal(true);
done();
})
.otherwise(function () {
permissableStub.restore();
errors.logError(new Error("Did not allow testUser"));
});
});
it('can use permissable function on Model to forbid something', function (done) {
var testUser,
permissableStub = sinon.stub(PostProvider, 'permissable', function () {
return when.reject();
});
// createTestUser()
UserProvider.browse()
.then(function (foundUser) {
testUser = foundUser.models[0];
return permissions.canThis(testUser).edit.post(123);
})
.then(function () {
permissableStub.restore();
errors.logError(new Error("Allowed testUser to edit post"));
})
.otherwise(function () {
permissableStub.restore();
permissableStub.calledWith(123, testUser.id, 'edit').should.equal(true);
done();
});
});
});
}());
it('does not allow edit post without permission', function (done) {
var fakePage = {
id: 1
};
createTestPermissions()
.then(permissions.init)
.then(function () {
return Models.User.read({id: 1});
})
.then(function (foundUser) {
var canThisResult = permissions.canThis(foundUser);
should.exist(canThisResult.edit);
should.exist(canThisResult.edit.post);
return canThisResult.edit.page(fakePage);
})
.then(function () {
errors.logError(new Error("Allowed edit post without permission"));
}, function () {
done();
});
});
it('allows edit post with permission', function (done) {
var fakePost = {
id: "1"
};
createTestPermissions()
.then(permissions.init)
.then(function () {
return Models.User.read({id: 1});
})
.then(function (foundUser) {
var newPerm = new Models.Permission({
name: "test3 edit post",
action_type: "edit",
object_type: "post"
});
return newPerm.save().then(function () {
return foundUser.permissions().attach(newPerm);
});
})
.then(function () {
return Models.User.read({id: 1}, { withRelated: ['permissions']});
})
.then(function (updatedUser) {
// TODO: Verify updatedUser.related('permissions') has the permission?
var canThisResult = permissions.canThis(updatedUser);
should.exist(canThisResult.edit);
should.exist(canThisResult.edit.post);
return canThisResult.edit.post(fakePost);
})
.then(function () {
done();
}, function () {
errors.logError(new Error("Did not allow edit post with permission"));
});
});
it('can use permissable function on Model to allow something', function (done) {
var testUser,
permissableStub = sinon.stub(PostProvider, 'permissable', function () {
return when.resolve();
});
// createTestUser()
UserProvider.browse()
.then(function (foundUser) {
testUser = foundUser.models[0];
return permissions.canThis(testUser).edit.post(123);
})
.then(function () {
permissableStub.restore();
permissableStub.calledWith(123, testUser.id, 'edit').should.equal(true);
done();
})
.otherwise(function () {
permissableStub.restore();
errors.logError(new Error("Did not allow testUser"));
});
});
it('can use permissable function on Model to forbid something', function (done) {
var testUser,
permissableStub = sinon.stub(PostProvider, 'permissable', function () {
return when.reject();
});
// createTestUser()
UserProvider.browse()
.then(function (foundUser) {
testUser = foundUser.models[0];
return permissions.canThis(testUser).edit.post(123);
})
.then(function () {
permissableStub.restore();
errors.logError(new Error("Allowed testUser to edit post"));
})
.otherwise(function () {
permissableStub.restore();
permissableStub.calledWith(123, testUser.id, 'edit').should.equal(true);
done();
});
});
});