0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-04-08 02:52:39 -05:00

Merge branch '0.3.3-wip'

Conflicts:
	core/client/views/blog.js
	core/server/api.js
	core/server/views/default.hbs
	package.json
This commit is contained in:
Hannah Wolfe 2013-10-20 10:09:39 +01:00
commit 65dcb17117
28 changed files with 248 additions and 173 deletions

View file

@ -64,6 +64,9 @@
$dropzone.find('.js-fileupload').fileupload().fileupload("option", {
url: '/ghost/upload/',
headers: {
'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
},
add: function (e, data) {
$dropzone.find('.js-fileupload').removeClass('right');
$dropzone.find('.js-url, button.centre').remove();

View file

@ -23,6 +23,16 @@
_.extend(Ghost, Backbone.Events);
Backbone.oldsync = Backbone.sync;
// override original sync method to make header request contain csrf token
Backbone.sync = function (method, model, options, error) {
options.beforeSend = function (xhr) {
xhr.setRequestHeader('X-CSRF-Token', $("meta[name='csrf-param']").attr('content'));
};
/* call the old sync method */
return Backbone.oldsync(method, model, options, error);
};
Ghost.init = function () {
Ghost.router = new Ghost.Router();

View file

@ -202,6 +202,9 @@
if (self.className.indexOf('notification-persistent') !== -1) {
$.ajax({
type: "DELETE",
headers: {
'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
},
url: '/api/v0.1/notifications/' + $(self).find('.close').data('id')
}).done(function (result) {
bbSelf.$el.slideUp(250, function () {
@ -231,6 +234,9 @@
bbSelf = this;
$.ajax({
type: "DELETE",
headers: {
'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
},
url: '/api/v0.1/notifications/' + $(self).data('id')
}).done(function (result) {
var height = bbSelf.$('.js-notification').outerHeight(true),

View file

@ -46,7 +46,7 @@
return Backbone.trigger('blog:activeItem', null);
}
var id = this.collection.at(0).id;
var id = this.collection.at(0) ? this.collection.at(0).id : false;
if (id) {
Backbone.trigger('blog:activeItem', id);
}
@ -87,6 +87,8 @@
// Load moar posts!
this.isLoading = true;
this.collection.fetch({
update: true,
remove: false,
data: {
status: 'all',
page: (self.collection.currentPage + 1),

View file

@ -33,6 +33,9 @@
$.ajax({
url: '/ghost/signin/',
type: 'POST',
headers: {
'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
},
data: {
email: email,
password: password,
@ -87,6 +90,9 @@
$.ajax({
url: '/ghost/signup/',
type: 'POST',
headers: {
'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
},
data: {
name: name,
email: email,
@ -136,6 +142,9 @@
$.ajax({
url: '/ghost/forgotten/',
type: 'POST',
headers: {
'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
},
data: {
email: email
},

View file

@ -204,8 +204,8 @@
var self = this, upload = new Ghost.Models.uploadModal({'key': key, 'src': src, 'id': this.id, 'accept': {
func: function () { // The function called on acceptance
var data = {};
if (this.$('#uploadurl').val()) {
data[key] = this.$('#uploadurl').val();
if (this.$('.js-upload-url').val()) {
data[key] = this.$('.js-upload-url').val();
} else {
data[key] = this.$('.js-upload-target').attr('src');
}
@ -266,8 +266,8 @@
var self = this, upload = new Ghost.Models.uploadModal({'key': key, 'src': src, 'id': this.id, 'accept': {
func: function () { // The function called on acceptance
var data = {};
if (this.$('#uploadurl').val()) {
data[key] = this.$('#uploadurl').val();
if (this.$('.js-upload-url').val()) {
data[key] = this.$('.js-upload-url').val();
} else {
data[key] = this.$('.js-upload-target').attr('src');
}
@ -379,6 +379,9 @@
$.ajax({
url: '/ghost/changepw/',
type: 'POST',
headers: {
'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
},
data: {
password: oldPassword,
newpassword: newPassword,

View file

@ -111,6 +111,7 @@ function ghostLocals(req, res, next) {
res.locals = res.locals || {};
res.locals.version = packageInfo.version;
res.locals.path = req.path;
res.locals.csrfToken = req.session._csrf;
if (res.isAdmin) {
_.extend(res.locals, {
@ -120,9 +121,9 @@ function ghostLocals(req, res, next) {
api.users.read({id: req.session.user}).then(function (currentUser) {
_.extend(res.locals, {
currentUser: {
name: currentUser.attributes.name,
email: currentUser.attributes.email,
image: currentUser.attributes.image
name: currentUser.name,
email: currentUser.email,
image: currentUser.image
}
});
next();
@ -277,9 +278,16 @@ when(ghost.init()).then(function () {
server.use('/ghost/upload/', express.multipart());
server.use('/ghost/upload/', express.multipart({uploadDir: __dirname + '/content/images'}));
server.use('/ghost/debug/db/import/', express.multipart());
server.use(express.cookieParser(ghost.dbHash));
server.use(express.cookieSession({ cookie: { maxAge: 60000000 }}));
// Session handling
// Pro tip: while in development mode cookieSession can be used
// to keep you logged in while restarting the server
server.use(express.cookieParser(ghost.dbHash));
server.use(express.cookieSession({ cookie : { maxAge: 12 * 60 * 60 * 1000 }}));
//enable express csrf protection
server.use(express.csrf());
// local data
server.use(ghostLocals);
// So on every request we actually clean out reduntant passive notifications from the server side
@ -296,10 +304,10 @@ when(ghost.init()).then(function () {
// ### Error handling
// 404 Handler
server.use(errors.render404Page);
server.use(errors.error404);
// 500 Handler
server.use(errors.render500Page);
server.use(errors.error500);
// ## Routing
@ -349,9 +357,8 @@ when(ghost.init()).then(function () {
server.get('/ghost/debug/', auth, admin.debug.index);
server.get('/ghost/debug/db/export/', auth, admin.debug['export']);
server.post('/ghost/debug/db/import/', auth, admin.debug['import']);
server.get('/ghost/debug/db/reset/', auth, admin.debug.reset);
// We don't want to register bodyParser globally b/c of security concerns, so use multipart only here
server.post('/ghost/upload/', admin.uploader);
server.post('/ghost/upload/', auth, admin.uploader);
// redirect to /ghost and let that do the authentication to prevent redirects to /ghost//admin etc.
server.get(/^\/((ghost-admin|admin|wp-admin|dashboard|signin)\/?)/, function (req, res) {
res.redirect('/ghost/');

View file

@ -18,7 +18,8 @@ var Ghost = require('../ghost'),
requestHandler,
settingsObject,
settingsCollection,
settingsFilter;
settingsFilter,
filteredUserAttributes = ['password', 'created_by', 'updated_by'];
// ## Posts
posts = {
@ -27,7 +28,17 @@ posts = {
// **takes:** filter / pagination parameters
browse: function browse(options) {
// **returns:** a promise for a page of posts in a json object
return dataProvider.Post.findPage(options);
//return dataProvider.Post.findPage(options);
return dataProvider.Post.findPage(options).then(function (result) {
var i = 0,
omitted = result;
for (i = 0; i < omitted.posts.length; i = i + 1) {
omitted.posts[i].author = _.omit(omitted.posts[i].author, filteredUserAttributes);
omitted.posts[i].user = _.omit(omitted.posts[i].user, filteredUserAttributes);
}
return omitted;
});
},
// #### Read
@ -35,7 +46,19 @@ posts = {
// **takes:** an identifier (id or slug?)
read: function read(args) {
// **returns:** a promise for a single post in a json object
return dataProvider.Post.findOne(args);
return dataProvider.Post.findOne(args).then(function (result) {
var omitted;
if (result) {
omitted = result.toJSON();
omitted.author = _.omit(omitted.author, filteredUserAttributes);
omitted.user = _.omit(omitted.user, filteredUserAttributes);
return omitted;
}
return null;
});
},
// #### Edit
@ -83,8 +106,8 @@ posts = {
return when(posts.read({id : args.id})).then(function (result) {
return dataProvider.Post.destroy(args.id).then(function () {
var deletedObj = {};
deletedObj.id = result.attributes.id;
deletedObj.slug = result.attributes.slug;
deletedObj.id = result.id;
deletedObj.slug = result.slug;
return deletedObj;
});
});
@ -101,7 +124,21 @@ users = {
// **takes:** options object
browse: function browse(options) {
// **returns:** a promise for a collection of users in a json object
return dataProvider.User.browse(options);
return dataProvider.User.browse(options).then(function (result) {
var i = 0,
omitted = {};
if (result) {
omitted = result.toJSON();
}
for (i = 0; i < omitted.length; i = i + 1) {
omitted[i] = _.omit(omitted[i], filteredUserAttributes);
}
return omitted;
});
},
// #### Read
@ -113,10 +150,13 @@ users = {
args = {id: this.user};
}
var filteredAttributes = ['password', 'created_by', 'updated_by'];
return dataProvider.User.read(args).then(function (result) {
if (result) {
var omitted = _.omit(result.toJSON(), filteredUserAttributes);
return omitted;
}
return dataProvider.User.read(args).then(function omitAttrs(result) {
return _.omit(result, filteredAttributes);
return null;
});
},

View file

@ -107,12 +107,14 @@ adminControllers = {
});
}
if (type === 'image/jpeg' || type === 'image/png' || type === 'image/gif') {
//limit uploads to type && extension
if ((type === 'image/jpeg' || type === 'image/png' || type === 'image/gif')
&& (ext === '.jpg' || ext === '.jpeg' || ext === '.png' || ext === '.gif')) {
getUniqueFileName(dir, basename, ext, null, function (filename) {
renameFile(filename);
});
} else {
res.send(404, 'Invalid filetype');
res.send(403, 'Invalid file type');
}
},
'login': function (req, res) {
@ -144,7 +146,6 @@ adminControllers = {
} else {
res.json(401, {error: 'Slow down, there are way too many login attempts!'});
}
},
changepw: function (req, res) {
api.users.changePassword({
@ -229,7 +230,7 @@ adminControllers = {
}).otherwise(errors.logAndThrowError);
},
'logout': function (req, res) {
delete req.session.user;
req.session = null;
var notification = {
type: 'success',
message: 'You were successfully signed out',
@ -376,7 +377,7 @@ adminControllers = {
};
return api.notifications.add(notification).then(function () {
delete req.session.user;
req.session = null;
res.set({
"X-Cache-Invalidate": "/*"
});
@ -392,37 +393,6 @@ adminControllers = {
id: 'per-' + (ghost.notifications.length + 1)
};
return api.notifications.add(notification).then(function () {
res.redirect('/ghost/debug/');
});
});
},
'reset': function (req, res) {
// Grab the current version so we can get the migration
dataProvider.reset()
.then(function resetSuccess() {
var notification = {
type: 'success',
message: "Database reset. Create a new user",
status: 'persistent',
id: 'per-' + (ghost.notifications.length + 1)
};
return api.notifications.add(notification).then(function () {
delete req.session.user;
res.set({
"X-Cache-Invalidate": "/*"
});
res.redirect('/ghost/signup/');
});
}, function resetFailure(error) {
var notification = {
type: 'error',
message: error.message || error,
status: 'persistent',
id: 'per-' + (ghost.notifications.length + 1)
};
return api.notifications.add(notification).then(function () {
res.redirect('/ghost/debug/');
});

View file

@ -66,7 +66,7 @@ frontendControllers = {
'single': function (req, res, next) {
api.posts.read({'slug': req.params.slug}).then(function (post) {
if (post) {
ghost.doFilter('prePostsRender', post.toJSON(), function (post) {
ghost.doFilter('prePostsRender', post, function (post) {
res.render('post', {post: post});
});
} else {
@ -88,7 +88,7 @@ frontendControllers = {
title: ghost.settings('title'),
description: ghost.settings('description'),
generator: 'Ghost v' + res.locals.version,
author: user ? user.attributes.name : null,
author: user ? user.name : null,
feed_url: url.resolve(siteUrl, '/rss/'),
site_url: siteUrl,
ttl: '60'

View file

@ -2,21 +2,21 @@ var when = require('when'),
_ = require('underscore'),
migration = require('../migration'),
client = require('../../models/base').client,
knex = require('../../models/base').Knex,
knex = require('../../models/base').knex,
exporter;
function getTablesFromSqlite3() {
return knex.Raw("select * from sqlite_master where type = 'table'").then(function (response) {
return _.reject(_.pluck(response, 'tbl_name'), function (name) {
return knex.raw("select * from sqlite_master where type = 'table'").then(function (response) {
return _.reject(_.pluck(response[0], 'tbl_name'), function (name) {
return name === 'sqlite_sequence';
});
});
}
function getTablesFromMySQL() {
return knex.Raw('show tables').then(function (response) {
return _.flatten(_.map(response, function (entry) {
return knex.raw("show tables").then(function (response) {
return _.flatten(_.map(response[0], function (entry) {
return _.values(entry);
}));
});

View file

@ -1,5 +1,5 @@
var when = require('when'),
knex = require('../../models/base').Knex,
knex = require('../../models/base').knex,
up,
down;
@ -7,7 +7,7 @@ up = function () {
return when.all([
knex.Schema.createTable('posts', function (t) {
knex.schema.createTable('posts', function (t) {
t.increments().primary();
t.string('uuid', 36).notNull();
t.string('title', 150).notNull();
@ -30,7 +30,7 @@ up = function () {
t.integer('published_by').nullable();
}),
knex.Schema.createTable('users', function (t) {
knex.schema.createTable('users', function (t) {
t.increments().primary();
t.string('uuid', 36).notNull();
t.string('name', 150).notNull();
@ -54,7 +54,7 @@ up = function () {
t.integer('updated_by').nullable();
}),
knex.Schema.createTable('roles', function (t) {
knex.schema.createTable('roles', function (t) {
t.increments().primary();
t.string('uuid', 36).notNull();
t.string('name', 150).notNull();
@ -65,13 +65,13 @@ up = function () {
t.integer('updated_by').nullable();
}),
knex.Schema.createTable('roles_users', function (t) {
knex.schema.createTable('roles_users', function (t) {
t.increments().primary();
t.integer('role_id').notNull();
t.integer('user_id').notNull();
}),
knex.Schema.createTable('permissions', function (t) {
knex.schema.createTable('permissions', function (t) {
t.increments().primary();
t.string('uuid', 36).notNull();
t.string('name', 150).notNull();
@ -84,19 +84,19 @@ up = function () {
t.integer('updated_by').nullable();
}),
knex.Schema.createTable('permissions_users', function (t) {
knex.schema.createTable('permissions_users', function (t) {
t.increments().primary();
t.integer('user_id').notNull();
t.integer('permission_id').notNull();
}),
knex.Schema.createTable('permissions_roles', function (t) {
knex.schema.createTable('permissions_roles', function (t) {
t.increments().primary();
t.integer('role_id').notNull();
t.integer('permission_id').notNull();
}),
knex.Schema.createTable('settings', function (t) {
knex.schema.createTable('settings', function (t) {
t.increments().primary();
t.string('uuid', 36).notNull();
t.string('key', 150).notNull().unique();
@ -107,7 +107,7 @@ up = function () {
t.dateTime('updated_at').nullable();
t.integer('updated_by').nullable();
}),
knex.Schema.createTable('tags', function (t) {
knex.schema.createTable('tags', function (t) {
t.increments().primary();
t.string('uuid', 36).notNull();
t.string('name', 150).notNull();
@ -120,30 +120,31 @@ up = function () {
t.integer('created_by').notNull();
t.dateTime('updated_at').nullable();
t.integer('updated_by').nullable();
}),
knex.Schema.createTable('posts_tags', function (t) {
})
]).then(function () {
return knex.schema.createTable('posts_tags', function (t) {
t.increments().primary();
t.integer('post_id').notNull().unsigned().references('id').inTable('posts');
t.integer('tag_id').notNull().unsigned().references('id').inTable('tags');
})
]);
});
});
};
down = function () {
return when.all([
knex.Schema.dropTableIfExists('posts_tags'),
knex.Schema.dropTableIfExists('roles_users'),
knex.Schema.dropTableIfExists('permissions_users'),
knex.Schema.dropTableIfExists('permissions_roles'),
knex.Schema.dropTableIfExists('users')
knex.schema.dropTableIfExists('posts_tags'),
knex.schema.dropTableIfExists('roles_users'),
knex.schema.dropTableIfExists('permissions_users'),
knex.schema.dropTableIfExists('permissions_roles'),
knex.schema.dropTableIfExists('users')
]).then(function () {
return when.all([
knex.Schema.dropTableIfExists('roles'),
knex.Schema.dropTableIfExists('settings'),
knex.Schema.dropTableIfExists('permissions'),
knex.Schema.dropTableIfExists('tags'),
knex.Schema.dropTableIfExists('posts')
knex.schema.dropTableIfExists('roles'),
knex.schema.dropTableIfExists('settings'),
knex.schema.dropTableIfExists('permissions'),
knex.schema.dropTableIfExists('tags'),
knex.schema.dropTableIfExists('posts')
]);
});
};

View file

@ -3,7 +3,7 @@ var _ = require('underscore'),
when = require('when'),
series = require('when/sequence'),
errors = require('../../errorHandling'),
knex = require('../../models/base').Knex,
knex = require('../../models/base').knex,
defaultSettings = require('../default-settings'),
Settings = require('../../models/settings').Settings,
@ -30,7 +30,7 @@ function getDefaultDatabaseVersion() {
// The migration version number according to the database
// This is what the database is currently at and may need to be updated
function getDatabaseVersion() {
return knex.Schema.hasTable('settings').then(function (exists) {
return knex.schema.hasTable('settings').then(function (exists) {
// Check for the current version from the settings table
if (exists) {
// Temporary code to deal with old databases with currentVersion settings

View file

@ -29,6 +29,26 @@ errors = {
throw err;
},
logWarn: function (warn, context, help) {
if ((process.env.NODE_ENV === 'development' ||
process.env.NODE_ENV === 'staging' ||
process.env.NODE_ENV === 'production')) {
console.log('\nWarning:'.yellow, warn.yellow);
if (context) {
console.log(context.white);
}
if (help) {
console.log(help.green);
}
// add a new line
console.log('');
}
},
logError: function (err, context, help) {
var stack = err ? err.stack : null;
err = err.message || err || 'Unknown';
@ -41,7 +61,7 @@ errors = {
console.error('\nERROR:'.red, err.red);
if (context) {
console.error(context);
console.error(context.white);
}
if (help) {
@ -133,7 +153,7 @@ errors = {
}
// Are we admin? If so, don't worry about the user template
if (res.isAdmin || userErrorTemplateExists === true) {
if ((res.isAdmin && req.session.user) || userErrorTemplateExists === true) {
return renderErrorInt();
}
@ -150,27 +170,29 @@ errors = {
return renderErrorInt();
}
// Message only displays the first time an error is triggered.
errors.logError(
"Theme error template not found",
null,
"Add an error.hbs template to the theme for customised errors."
);
renderErrorInt(defaultErrorTemplatePath);
});
},
render404Page: function (req, res, next) {
var message = res.isAdmin ? "No Ghost Found" : "Page Not Found";
this.renderErrorPage(404, message, req, res, next);
error404: function (req, res, next) {
var message = res.isAdmin && req.session.user ? "No Ghost Found" : "Page Not Found";
if (req.method === 'GET') {
this.renderErrorPage(404, message, req, res, next);
} else {
res.send(404, message);
}
},
render500Page: function (err, req, res, next) {
if (!err || !(err instanceof Error)) {
next();
error500: function (err, req, res, next) {
if (req.method === 'GET') {
if (!err || !(err instanceof Error)) {
next();
}
errors.renderErrorPage(500, err, req, res, next);
} else {
res.send(500, err);
}
errors.renderErrorPage(500, err, req, res, next);
}
};
@ -182,8 +204,8 @@ _.bindAll(
'logAndThrowError',
'logErrorWithRedirect',
'renderErrorPage',
'render404Page',
'render500Page'
'error404',
'error500'
);
module.exports = errors;

View file

@ -1,4 +1,4 @@
var GhostBookshelf,
var ghostBookshelf,
Bookshelf = require('bookshelf'),
when = require('when'),
moment = require('moment'),
@ -8,16 +8,15 @@ var GhostBookshelf,
Validator = require('validator').Validator,
sanitize = require('validator').sanitize;
// 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[process.env.NODE_ENV || 'development'].database);
GhostBookshelf.client = config[process.env.NODE_ENV].database.client;
// Initializes a new Bookshelf instance, for reference elsewhere in Ghost.
ghostBookshelf = Bookshelf.initialize(config[process.env.NODE_ENV || 'development'].database);
ghostBookshelf.client = config[process.env.NODE_ENV].database.client;
GhostBookshelf.validator = new Validator();
ghostBookshelf.validator = new Validator();
// 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({
ghostBookshelf.Model = ghostBookshelf.Model.extend({
hasTimestamps: true,
@ -145,7 +144,7 @@ GhostBookshelf.Model = GhostBookshelf.Model.extend({
*/
findAll: function (options) {
options = options || {};
return GhostBookshelf.Collection.forge([], {model: this}).fetch(options);
return ghostBookshelf.Collection.forge([], {model: this}).fetch(options);
},
browse: function () {
@ -212,4 +211,4 @@ GhostBookshelf.Model = GhostBookshelf.Model.extend({
});
module.exports = GhostBookshelf;
module.exports = ghostBookshelf;

View file

@ -1,10 +1,10 @@
var GhostBookshelf = require('./base'),
var ghostBookshelf = require('./base'),
User = require('./user').User,
Role = require('./role').Role,
Permission,
Permissions;
Permission = GhostBookshelf.Model.extend({
Permission = ghostBookshelf.Model.extend({
tableName: 'permissions',
@ -14,7 +14,7 @@ Permission = GhostBookshelf.Model.extend({
validate: function () {
// TODO: validate object_type, action_type and object_id
GhostBookshelf.validator.check(this.get('name'), "Permission name cannot be blank").notEmpty();
ghostBookshelf.validator.check(this.get('name'), "Permission name cannot be blank").notEmpty();
},
roles: function () {
@ -26,7 +26,7 @@ Permission = GhostBookshelf.Model.extend({
}
});
Permissions = GhostBookshelf.Collection.extend({
Permissions = ghostBookshelf.Collection.extend({
model: Permission
});

View file

@ -11,9 +11,9 @@ var Post,
config = require('../../../config'),
Tag = require('./tag').Tag,
Tags = require('./tag').Tags,
GhostBookshelf = require('./base');
ghostBookshelf = require('./base');
Post = GhostBookshelf.Model.extend({
Post = ghostBookshelf.Model.extend({
tableName: 'posts',
@ -38,7 +38,7 @@ Post = GhostBookshelf.Model.extend({
},
validate: function () {
GhostBookshelf.validator.check(this.get('title'), "Post title cannot be blank").notEmpty();
ghostBookshelf.validator.check(this.get('title'), "Post title cannot be blank").notEmpty();
return true;
},
@ -61,7 +61,7 @@ Post = GhostBookshelf.Model.extend({
this.set('published_by', 1);
}
GhostBookshelf.Model.prototype.saving.call(this);
ghostBookshelf.Model.prototype.saving.call(this);
if (this.hasChanged('slug')) {
// Pass the new slug through the generator to strip illegal characters, detect duplicates
@ -80,7 +80,7 @@ Post = GhostBookshelf.Model.extend({
this.set('author_id', 1);
}
GhostBookshelf.Model.prototype.creating.call(this);
ghostBookshelf.Model.prototype.creating.call(this);
if (!this.get('slug')) {
// Generating a slug requires a db call to look for conflicting slugs
@ -174,7 +174,7 @@ Post = GhostBookshelf.Model.extend({
findAll: function (options) {
options = options || {};
options.withRelated = [ 'author', 'user', 'tags' ];
return GhostBookshelf.Model.findAll.call(this, options);
return ghostBookshelf.Model.findAll.call(this, options);
},
// #### findOne
@ -182,7 +182,7 @@ Post = GhostBookshelf.Model.extend({
findOne: function (args, options) {
options = options || {};
options.withRelated = [ 'author', 'user', 'tags' ];
return GhostBookshelf.Model.findOne.call(this, args, options);
return ghostBookshelf.Model.findOne.call(this, args, options);
},
// #### findPage
@ -251,7 +251,7 @@ Post = GhostBookshelf.Model.extend({
// After we're done, we need to figure out what
// the limits are for the pagination values.
qb = GhostBookshelf.Knex(_.result(collection, 'tableName'));
qb = ghostBookshelf.knex(_.result(collection, 'tableName'));
if (opts.where) {
qb.where(opts.where);
@ -328,7 +328,7 @@ Post = GhostBookshelf.Model.extend({
},
add: function (newPostData, options) {
return GhostBookshelf.Model.add.call(this, newPostData, options).tap(function (post) {
return ghostBookshelf.Model.add.call(this, newPostData, options).tap(function (post) {
// associated models can't be created until the post has an ID, so run this after
return post.updateTags(newPostData.tags);
});
@ -349,7 +349,7 @@ Post = GhostBookshelf.Model.extend({
});
Posts = GhostBookshelf.Collection.extend({
Posts = ghostBookshelf.Collection.extend({
model: Post

View file

@ -1,18 +1,18 @@
var User = require('./user').User,
Permission = require('./permission').Permission,
GhostBookshelf = require('./base'),
ghostBookshelf = require('./base'),
Role,
Roles;
Role = GhostBookshelf.Model.extend({
Role = ghostBookshelf.Model.extend({
tableName: 'roles',
permittedAttributes: ['id', 'uuid', 'name', 'description', 'created_at', 'created_by', 'updated_at', 'updated_by'],
validate: function () {
GhostBookshelf.validator.check(this.get('name'), "Role name cannot be blank").notEmpty();
GhostBookshelf.validator.check(this.get('description'), "Role description cannot be blank").notEmpty();
ghostBookshelf.validator.check(this.get('name'), "Role name cannot be blank").notEmpty();
ghostBookshelf.validator.check(this.get('description'), "Role description cannot be blank").notEmpty();
},
users: function () {
@ -24,7 +24,7 @@ Role = GhostBookshelf.Model.extend({
}
});
Roles = GhostBookshelf.Collection.extend({
Roles = ghostBookshelf.Collection.extend({
model: Role
});

View file

@ -1,6 +1,6 @@
var Settings,
GhostBookshelf = require('./base'),
validator = GhostBookshelf.validator,
ghostBookshelf = require('./base'),
validator = ghostBookshelf.validator,
uuid = require('node-uuid'),
_ = require('underscore'),
errors = require('../errorHandling'),
@ -29,7 +29,7 @@ defaultSettings = parseDefaultSettings();
// 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({
Settings = ghostBookshelf.Model.extend({
tableName: 'settings',
@ -83,7 +83,7 @@ Settings = GhostBookshelf.Model.extend({
this.set('value', this.sanitize('value'));
}
return GhostBookshelf.Model.prototype.saving.apply(this, arguments);
return ghostBookshelf.Model.prototype.saving.apply(this, arguments);
}
}, {
@ -92,7 +92,7 @@ Settings = GhostBookshelf.Model.extend({
if (!_.isObject(_key)) {
_key = { key: _key };
}
return GhostBookshelf.Model.read.call(this, _key);
return ghostBookshelf.Model.read.call(this, _key);
},
edit: function (_data) {

View file

@ -1,9 +1,9 @@
var Tag,
Tags,
Posts = require('./post').Posts,
GhostBookshelf = require('./base');
ghostBookshelf = require('./base');
Tag = GhostBookshelf.Model.extend({
Tag = ghostBookshelf.Model.extend({
tableName: 'tags',
@ -20,7 +20,7 @@ Tag = GhostBookshelf.Model.extend({
creating: function () {
var self = this;
GhostBookshelf.Model.prototype.creating.call(this);
ghostBookshelf.Model.prototype.creating.call(this);
if (!this.get('slug')) {
// Generating a slug requires a db call to look for conflicting slugs
@ -36,7 +36,7 @@ Tag = GhostBookshelf.Model.extend({
}
});
Tags = GhostBookshelf.Collection.extend({
Tags = ghostBookshelf.Collection.extend({
model: Tag

View file

@ -7,14 +7,14 @@ var User,
nodefn = require('when/node/function'),
bcrypt = require('bcrypt-nodejs'),
Posts = require('./post').Posts,
GhostBookshelf = require('./base'),
ghostBookshelf = require('./base'),
Role = require('./role').Role,
Permission = require('./permission').Permission;
function validatePasswordLength(password) {
try {
GhostBookshelf.validator.check(password, "Your must be at least 8 characters long.").len(8);
ghostBookshelf.validator.check(password, "Your must be at least 8 characters long.").len(8);
} catch (error) {
return when.reject(error);
}
@ -22,7 +22,7 @@ function validatePasswordLength(password) {
return when.resolve();
}
User = GhostBookshelf.Model.extend({
User = ghostBookshelf.Model.extend({
tableName: 'users',
@ -33,10 +33,10 @@ User = GhostBookshelf.Model.extend({
],
validate: function () {
GhostBookshelf.validator.check(this.get('email'), "Please enter a valid email address. That one looks a bit dodgy.").isEmail();
GhostBookshelf.validator.check(this.get('bio'), "We're not writing a novel here! I'm afraid your bio has to stay under 200 characters.").len(0, 200);
ghostBookshelf.validator.check(this.get('email'), "Please enter a valid email address. That one looks a bit dodgy.").isEmail();
ghostBookshelf.validator.check(this.get('bio'), "We're not writing a novel here! I'm afraid your bio has to stay under 200 characters.").len(0, 200);
if (this.get('website') && this.get('website').length > 0) {
GhostBookshelf.validator.check(this.get('website'), "Looks like your website is not actually a website. Try again?").isUrl();
ghostBookshelf.validator.check(this.get('website'), "Looks like your website is not actually a website. Try again?").isUrl();
}
return true;
},
@ -44,7 +44,7 @@ User = GhostBookshelf.Model.extend({
creating: function () {
var self = this;
GhostBookshelf.Model.prototype.creating.call(this);
ghostBookshelf.Model.prototype.creating.call(this);
if (!this.get('slug')) {
// Generating a slug requires a db call to look for conflicting slugs
@ -63,7 +63,7 @@ User = GhostBookshelf.Model.extend({
this.set('website', this.sanitize('website'));
this.set('bio', this.sanitize('bio'));
return GhostBookshelf.Model.prototype.saving.apply(this, arguments);
return ghostBookshelf.Model.prototype.saving.apply(this, arguments);
},
posts: function () {
@ -111,7 +111,7 @@ User = GhostBookshelf.Model.extend({
// Assign the hashed password
userData.password = hash;
// Save the user with the hashed password
return GhostBookshelf.Model.add.call(self, userData);
return ghostBookshelf.Model.add.call(self, userData);
}).then(function (addedUser) {
// Assign the userData to our created user so we can pass it back
userData = addedUser;
@ -135,8 +135,8 @@ User = GhostBookshelf.Model.extend({
// }
// 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);
// ghostBookshelf.Model.add.call(UserRole, userRoles);
// return ghostBookshelf.Model.add.call(User, userData);
// }, errors.logAndThrowError);
// }, errors.logAndThrowError);
@ -239,7 +239,7 @@ User = GhostBookshelf.Model.extend({
});
Users = GhostBookshelf.Collection.extend({
Users = ghostBookshelf.Collection.extend({
model: User
});

View file

@ -16,6 +16,7 @@
</header>
<section class="content">
<form id="settings-export">
<input type="hidden" name="_csrf" value="{{csrfToken}}" />
<fieldset>
<div class="form-group">
<label>Export</label>
@ -25,11 +26,12 @@
</fieldset>
</form>
<form id="settings-import" method="post" action="/ghost/debug/db/import/" enctype="multipart/form-data">
<input type="hidden" name="_csrf" value="{{csrfToken}}" />
<fieldset>
<div class="form-group">
<label>Import</label>
<input type="file" class="button-add" name="importfile"></input>
<input type="submit" class="button-save" value="Import"></input>
<input type="file" class="button-add" name="importfile" />
<input type="submit" class="button-save" value="Import" />
<p>Import from another Ghost installation. If you import a user, this will replace the current user & log you out.</p>
</div>
</fieldset>

View file

@ -4,6 +4,7 @@
<head>
<meta http-equiv="Content-Type" content="text/html" charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="csrf-param" content="{{csrfToken}}">
<title>Ghost Admin</title>

View file

@ -31,26 +31,26 @@ describe('Admin Controller', function() {
});
describe('can not upload invalid file', function() {
it('should return 404 for invalid file type', function() {
it('should return 403 for invalid file type', function() {
res.send = sinon.stub();
req.files.uploadimage.name = 'INVALID.FILE';
req.files.uploadimage.type = 'application/octet-stream'
admin.uploader(req, res);
res.send.calledOnce.should.be.true;
res.send.args[0][0].should.equal(404);
res.send.args[0][1].should.equal('Invalid filetype');
res.send.args[0][0].should.equal(403);
res.send.args[0][1].should.equal('Invalid file type');
});
});
describe('can not upload file with valid extension but invalid type', function() {
it('should return 404 for invalid file type', function() {
it('should return 403 for invalid file type', function() {
res.send = sinon.stub();
req.files.uploadimage.name = 'INVALID.jpg';
req.files.uploadimage.type = 'application/octet-stream'
admin.uploader(req, res);
res.send.calledOnce.should.be.true;
res.send.args[0][0].should.equal(404);
res.send.args[0][1].should.equal('Invalid filetype');
res.send.args[0][0].should.equal(403);
res.send.args[0][1].should.equal('Invalid file type');
});
});

View file

@ -7,7 +7,7 @@ var testUtils = require('./testUtils'),
errors = require('../../server/errorHandling'),
// Stuff we are testing
knex = require("../../server/models/base").Knex,
knex = require("../../server/models/base").knex,
migration = require('../../server/data/migration'),
exporter = require('../../server/data/export'),
importer = require('../../server/data/import'),

View file

@ -5,7 +5,7 @@ var testUtils = require('./testUtils'),
// Stuff we are testing
Models = require('../../server/models'),
knex = require('../../server/models/base').Knex;
knex = require('../../server/models/base').knex;
describe('Settings Model', function () {

View file

@ -1,4 +1,4 @@
var knex = require('../../server/models/base').Knex,
var knex = require('../../server/models/base').knex,
when = require('when'),
migration = require("../../server/data/migration/"),
Settings = require('../../server/models/settings').Settings,

View file

@ -1,6 +1,6 @@
{
"name" : "ghost",
"version" : "0.3.2",
"version" : "0.3.3",
"description" : "Just a blogging platform.",
"author" : "Ghost Foundation",
"homepage" : "http://ghost.org",
@ -30,8 +30,8 @@
"underscore": "1.5.1",
"showdown": "0.3.1",
"sqlite3": "2.1.16",
"bookshelf": "0.3.1",
"knex": "0.2.7-alpha",
"bookshelf": "0.5.7",
"knex": "0.4.11",
"when": "2.2.1",
"bcrypt-nodejs": "0.0.3",
"node-uuid": "1.4.0",