mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
Merge branch '0.3.2-wip'
Conflicts: core/test/unit/api_posts_spec.js
This commit is contained in:
commit
9466a9753b
13 changed files with 155 additions and 79 deletions
|
@ -1 +1 @@
|
|||
Subproject commit c4c276653dc751e50fd927bd8c88a72930f4beff
|
||||
Subproject commit 10beda3f6c02921fa4644d8b33f30eefbf5af9ca
|
|
@ -1,5 +1,5 @@
|
|||
<a class="permalink{{#if featured}} featured{{/if}}" href="#">
|
||||
<h3 class="entry-title">{{title}}</h3>
|
||||
<h3 class="entry-title">{{{title}}}</h3>
|
||||
<section class="entry-meta">
|
||||
<time datetime="2013-01-04" class="date">
|
||||
{{#if published}}
|
||||
|
|
|
@ -285,6 +285,8 @@
|
|||
this.$('#entry-title').val(this.model.get('title')).focus();
|
||||
this.$('#entry-markdown').text(this.model.get('markdown'));
|
||||
|
||||
this.listenTo(this.model, 'change:title', this.renderTitle);
|
||||
|
||||
this.initMarkdown();
|
||||
this.renderPreview();
|
||||
|
||||
|
@ -365,6 +367,10 @@
|
|||
}
|
||||
},
|
||||
|
||||
renderTitle: function () {
|
||||
this.$('#entry-title').val(this.model.get('title'));
|
||||
},
|
||||
|
||||
// This is a hack to remove iOS6 white space on orientation change bug
|
||||
// See: http://cl.ly/RGx9
|
||||
orientationChange: function () {
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
this.addSubview(this.sidebar);
|
||||
|
||||
this.listenTo(Ghost.router, "route:settings", this.changePane);
|
||||
this.listenTo(Ghost.router, 'route:settings', this.changePane);
|
||||
},
|
||||
|
||||
changePane: function (pane) {
|
||||
|
@ -155,7 +155,8 @@
|
|||
},
|
||||
|
||||
saveSettings: function () {
|
||||
var title = this.$('#blog-title').val(),
|
||||
var self = this,
|
||||
title = this.$('#blog-title').val(),
|
||||
description = this.$('#blog-description').val(),
|
||||
email = this.$('#email-address').val(),
|
||||
postsPerPage = this.$('#postsPerPage').val();
|
||||
|
@ -186,7 +187,7 @@
|
|||
}, {
|
||||
success: this.saveSuccess,
|
||||
error: this.saveError
|
||||
});
|
||||
}).then(function () { self.render(); });
|
||||
}
|
||||
},
|
||||
showLogo: function (e) {
|
||||
|
@ -212,8 +213,10 @@
|
|||
self.model.save(data, {
|
||||
success: self.saveSuccess,
|
||||
error: self.saveError
|
||||
}).then(function () {
|
||||
self.render();
|
||||
});
|
||||
self.render();
|
||||
|
||||
return true;
|
||||
},
|
||||
buttonClass: "button-save right",
|
||||
|
@ -271,8 +274,9 @@
|
|||
self.model.save(data, {
|
||||
success: self.saveSuccess,
|
||||
error: self.saveError
|
||||
}).then(function () {
|
||||
self.render();
|
||||
});
|
||||
self.render();
|
||||
return true;
|
||||
},
|
||||
buttonClass: "button-save right",
|
||||
|
@ -311,7 +315,8 @@
|
|||
},
|
||||
|
||||
saveUser: function () {
|
||||
var userName = this.$('#user-name').val(),
|
||||
var self = this,
|
||||
userName = this.$('#user-name').val(),
|
||||
userEmail = this.$('#user-email').val(),
|
||||
userLocation = this.$('#user-location').val(),
|
||||
userWebsite = this.$('#user-website').val(),
|
||||
|
@ -350,6 +355,8 @@
|
|||
}, {
|
||||
success: this.saveSuccess,
|
||||
error: this.saveError
|
||||
}).then(function () {
|
||||
self.render();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -393,6 +400,8 @@
|
|||
status: 'passive'
|
||||
});
|
||||
}
|
||||
}).then(function () {
|
||||
self.render();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -41,6 +41,8 @@ function writeConfigFile() {
|
|||
|
||||
function validateConfigEnvironment() {
|
||||
var envVal = process.env.NODE_ENV || 'undefined',
|
||||
hasHostAndPort,
|
||||
hasSocket,
|
||||
config,
|
||||
parsedUrl;
|
||||
|
||||
|
@ -50,6 +52,7 @@ function validateConfigEnvironment() {
|
|||
|
||||
}
|
||||
|
||||
|
||||
// Check if we don't even have a config
|
||||
if (!config) {
|
||||
errors.logError(new Error('Cannot find the configuration for the current NODE_ENV'), "NODE_ENV=" + envVal, 'Ensure your config.js has a section for the current NODE_ENV value');
|
||||
|
@ -69,9 +72,12 @@ function validateConfigEnvironment() {
|
|||
return when.reject();
|
||||
}
|
||||
|
||||
hasHostAndPort = config.server && !!config.server.host && !!config.server.port;
|
||||
hasSocket = config.server && !!config.server.socket;
|
||||
|
||||
// Check for valid server host and port values
|
||||
if (!config.server || !config.server.host || !config.server.port) {
|
||||
errors.logError(new Error('Your server values (host and port) in config.js are invalid.'), JSON.stringify(config.server), 'Please provide them before restarting.');
|
||||
if (!config.server || !(hasHostAndPort || hasSocket)) {
|
||||
errors.logError(new Error('Your server values (socket, or host and port) in config.js are invalid.'), JSON.stringify(config.server), 'Please provide them before restarting.');
|
||||
return when.reject();
|
||||
}
|
||||
|
||||
|
|
140
core/server.js
140
core/server.js
|
@ -4,6 +4,7 @@ var express = require('express'),
|
|||
_ = require('underscore'),
|
||||
colors = require('colors'),
|
||||
semver = require('semver'),
|
||||
fs = require('fs'),
|
||||
slashes = require('connect-slashes'),
|
||||
errors = require('./server/errorHandling'),
|
||||
admin = require('./server/controllers/admin'),
|
||||
|
@ -366,68 +367,87 @@ when(ghost.init()).then(function () {
|
|||
server.get('/:slug/', frontend.single);
|
||||
server.get('/', frontend.homepage);
|
||||
|
||||
// Are we using sockets? Custom socket or the default?
|
||||
function getSocket() {
|
||||
if (ghost.config().server.hasOwnProperty('socket')) {
|
||||
return _.isString(ghost.config().server.socket) ? ghost.config().server.socket : path.join(__dirname, '../content/', process.env.NODE_ENV + '.socket');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function startGhost() {
|
||||
// Tell users if their node version is not supported, and exit
|
||||
if (!semver.satisfies(process.versions.node, packageInfo.engines.node)) {
|
||||
console.log(
|
||||
"\nERROR: Unsupported version of Node".red,
|
||||
"\nGhost needs Node version".red,
|
||||
packageInfo.engines.node.yellow,
|
||||
"you are using version".red,
|
||||
process.versions.node.yellow,
|
||||
"\nPlease go to http://nodejs.org to get the latest version".green
|
||||
);
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Startup & Shutdown messages
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
console.log(
|
||||
"Ghost is running...".green,
|
||||
"\nYour blog is now available on",
|
||||
ghost.config().url,
|
||||
"\nCtrl+C to shut down".grey
|
||||
);
|
||||
|
||||
// ensure that Ghost exits correctly on Ctrl+C
|
||||
process.on('SIGINT', function () {
|
||||
console.log(
|
||||
"\nGhost has shut down".red,
|
||||
"\nYour blog is now offline"
|
||||
);
|
||||
process.exit(0);
|
||||
});
|
||||
} else {
|
||||
console.log(
|
||||
"Ghost is running...".green,
|
||||
"\nListening on",
|
||||
getSocket() || ghost.config().server.host + ':' + ghost.config().server.port,
|
||||
"\nUrl configured as:",
|
||||
ghost.config().url,
|
||||
"\nCtrl+C to shut down".grey
|
||||
);
|
||||
// ensure that Ghost exits correctly on Ctrl+C
|
||||
process.on('SIGINT', function () {
|
||||
console.log(
|
||||
"\nGhost has shutdown".red,
|
||||
"\nGhost was running for",
|
||||
Math.round(process.uptime()),
|
||||
"seconds"
|
||||
);
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
// Let everyone know we have finished loading
|
||||
loading.resolve();
|
||||
}
|
||||
|
||||
// ## Start Ghost App
|
||||
server.listen(
|
||||
ghost.config().server.port,
|
||||
ghost.config().server.host,
|
||||
function () {
|
||||
if (getSocket()) {
|
||||
// Make sure the socket is gone before trying to create another
|
||||
fs.unlink(getSocket(), function (err) {
|
||||
server.listen(
|
||||
getSocket(),
|
||||
startGhost
|
||||
);
|
||||
fs.chmod(getSocket(), '0744');
|
||||
});
|
||||
|
||||
// Tell users if their node version is not supported, and exit
|
||||
if (!semver.satisfies(process.versions.node, packageInfo.engines.node)) {
|
||||
console.log(
|
||||
"\nERROR: Unsupported version of Node".red,
|
||||
"\nGhost needs Node version".red,
|
||||
packageInfo.engines.node.yellow,
|
||||
"you are using version".red,
|
||||
process.versions.node.yellow,
|
||||
"\nPlease go to http://nodejs.org to get the latest version".green
|
||||
);
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Startup & Shutdown messages
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
console.log(
|
||||
"Ghost is running...".green,
|
||||
"\nYour blog is now available on",
|
||||
ghost.config().url,
|
||||
"\nCtrl+C to shut down".grey
|
||||
);
|
||||
|
||||
// ensure that Ghost exits correctly on Ctrl+C
|
||||
process.on('SIGINT', function () {
|
||||
console.log(
|
||||
"\nGhost has shut down".red,
|
||||
"\nYour blog is now offline"
|
||||
);
|
||||
process.exit(0);
|
||||
});
|
||||
} else {
|
||||
console.log(
|
||||
"Ghost is running...".green,
|
||||
"\nListening on",
|
||||
ghost.config().server.host + ':' + ghost.config().server.port,
|
||||
"\nUrl configured as:",
|
||||
ghost.config().url,
|
||||
"\nCtrl+C to shut down".grey
|
||||
);
|
||||
// ensure that Ghost exits correctly on Ctrl+C
|
||||
process.on('SIGINT', function () {
|
||||
console.log(
|
||||
"\nGhost has shutdown".red,
|
||||
"\nGhost was running for",
|
||||
Math.round(process.uptime()),
|
||||
"seconds"
|
||||
);
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
// Let everyone know we have finished loading
|
||||
loading.resolve();
|
||||
}
|
||||
);
|
||||
} else {
|
||||
server.listen(
|
||||
ghost.config().server.port,
|
||||
ghost.config().server.host,
|
||||
startGhost
|
||||
);
|
||||
}
|
||||
}, errors.logAndThrowError);
|
||||
|
|
|
@ -191,7 +191,9 @@ coreHelpers = function (ghost) {
|
|||
});
|
||||
|
||||
ghost.registerThemeHelper('body_class', function (options) {
|
||||
var classes = [];
|
||||
var classes = [],
|
||||
tags = this.post && this.post.tags ? this.post.tags : this.tags || [];
|
||||
|
||||
if (_.isString(this.path) && this.path.match(/\/page/)) {
|
||||
classes.push('archive-template');
|
||||
} else if (!this.path || this.path === '/' || this.path === '') {
|
||||
|
@ -200,6 +202,10 @@ coreHelpers = function (ghost) {
|
|||
classes.push('post-template');
|
||||
}
|
||||
|
||||
if (tags) {
|
||||
classes = classes.concat(tags.map(function (tag) { return 'tag-' + tag.slug; }));
|
||||
}
|
||||
|
||||
return ghost.doFilter('body_class', classes, function (classes) {
|
||||
var classString = _.reduce(classes, function (memo, item) { return memo + ' ' + item; }, '');
|
||||
return new hbs.handlebars.SafeString(classString.trim());
|
||||
|
@ -207,10 +213,11 @@ coreHelpers = function (ghost) {
|
|||
});
|
||||
|
||||
ghost.registerThemeHelper('post_class', function (options) {
|
||||
var classes = ['post'];
|
||||
var classes = ['post'],
|
||||
tags = this.post && this.post.tags ? this.post.tags : this.tags || [];
|
||||
|
||||
if (this.tags) {
|
||||
classes = classes.concat(this.tags.map(function (tag) { return 'tag-' + tag.name; }));
|
||||
if (tags) {
|
||||
classes = classes.concat(tags.map(function (tag) { return 'tag-' + tag.slug; }));
|
||||
}
|
||||
|
||||
return ghost.doFilter('post_class', classes, function (classes) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<nav id="site-navigation" role="navigation">
|
||||
<ul>
|
||||
{{#links}}
|
||||
<li class="{{#active}}current-menu-item{{/active}}"><a title="{{title}}" href="{{url}}">{{title}}</a></li>
|
||||
<li class="{{#active}}current-menu-item{{/active}}"><a title="{{{title}}}" href="{{url}}">{{{title}}}</a></li>
|
||||
{{/links}}
|
||||
</ul>
|
||||
</nav>
|
|
@ -5,7 +5,8 @@ var GhostBookshelf,
|
|||
_ = require('underscore'),
|
||||
uuid = require('node-uuid'),
|
||||
config = require('../../../config'),
|
||||
Validator = require('validator').Validator;
|
||||
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.
|
||||
|
@ -78,6 +79,10 @@ GhostBookshelf.Model = GhostBookshelf.Model.extend({
|
|||
return attrs;
|
||||
},
|
||||
|
||||
sanitize: function (attr) {
|
||||
return sanitize(this.get(attr)).xss();
|
||||
},
|
||||
|
||||
// #### generateSlug
|
||||
// Create a string act as the permalink for an object.
|
||||
generateSlug: function (Model, base) {
|
||||
|
|
|
@ -51,7 +51,7 @@ Post = GhostBookshelf.Model.extend({
|
|||
|
||||
this.set('html', converter.makeHtml(this.get('markdown')));
|
||||
|
||||
this.set('title', this.get('title').trim());
|
||||
this.set('title', this.sanitize('title').trim());
|
||||
|
||||
if (this.hasChanged('status') && this.get('status') === 'published') {
|
||||
if (!this.get('published_at')) {
|
||||
|
|
|
@ -73,7 +73,19 @@ Settings = GhostBookshelf.Model.extend({
|
|||
validation[validationName].apply(validation, validationOptions);
|
||||
}, this);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
saving: function () {
|
||||
|
||||
// All blog setting keys that need their values to be escaped.
|
||||
if (this.get('type') === 'blog' && _.contains(['title', 'description', 'email'], this.get('key'))) {
|
||||
this.set('value', this.sanitize('value'));
|
||||
}
|
||||
|
||||
return GhostBookshelf.Model.prototype.saving.apply(this, arguments);
|
||||
}
|
||||
|
||||
}, {
|
||||
read: function (_key) {
|
||||
// Allow for just passing the key instead of attributes
|
||||
|
|
|
@ -55,6 +55,17 @@ User = GhostBookshelf.Model.extend({
|
|||
}
|
||||
},
|
||||
|
||||
saving: function () {
|
||||
|
||||
this.set('name', this.sanitize('name'));
|
||||
this.set('email', this.sanitize('email'));
|
||||
this.set('location', this.sanitize('location'));
|
||||
this.set('website', this.sanitize('website'));
|
||||
this.set('bio', this.sanitize('bio'));
|
||||
|
||||
return GhostBookshelf.Model.prototype.saving.apply(this, arguments);
|
||||
},
|
||||
|
||||
posts: function () {
|
||||
return this.hasMany(Posts, 'created_by');
|
||||
},
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
},
|
||||
"engineStrict": true,
|
||||
"dependencies": {
|
||||
"express": "3.3.4",
|
||||
"express-hbs": "0.2.2",
|
||||
"express": "3.4.0",
|
||||
"express-hbs": "0.3.0",
|
||||
"connect-slashes": "0.0.9",
|
||||
"node-polyglot": "0.2.1",
|
||||
"moment": "2.1.0",
|
||||
|
|
Loading…
Add table
Reference in a new issue