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/client/assets/lib/uploader.js
This commit is contained in:
commit
119b0ea430
11 changed files with 192 additions and 46 deletions
24
.afignore
24
.afignore
|
@ -1,24 +0,0 @@
|
||||||
#ignore database
|
|
||||||
b-cov
|
|
||||||
*.seed
|
|
||||||
*.log
|
|
||||||
*.csv
|
|
||||||
*.dat
|
|
||||||
*.out
|
|
||||||
*.pid
|
|
||||||
*.gz
|
|
||||||
|
|
||||||
pids
|
|
||||||
logs
|
|
||||||
results
|
|
||||||
|
|
||||||
npm-debug.log
|
|
||||||
|
|
||||||
.idea/*
|
|
||||||
*.iml
|
|
||||||
projectFilesBackup
|
|
||||||
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# Ghost DB file
|
|
||||||
*.db
|
|
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -38,7 +38,12 @@ projectFilesBackup
|
||||||
/core/server/data/export/exported*
|
/core/server/data/export/exported*
|
||||||
/docs
|
/docs
|
||||||
/_site
|
/_site
|
||||||
|
/content/data/*
|
||||||
|
/content/plugins/**/*
|
||||||
|
/content/themes/**/*
|
||||||
/content/images/**/*
|
/content/images/**/*
|
||||||
|
!/content/themes/casper/**
|
||||||
|
!README.md
|
||||||
|
|
||||||
# Changelog, which is autogenerated, not committed
|
# Changelog, which is autogenerated, not committed
|
||||||
CHANGELOG.md
|
CHANGELOG.md
|
||||||
|
|
34
Gruntfile.js
34
Gruntfile.js
|
@ -12,7 +12,16 @@ var path = require('path'),
|
||||||
buildGlob = [
|
buildGlob = [
|
||||||
'**',
|
'**',
|
||||||
'!docs/**',
|
'!docs/**',
|
||||||
|
'!_site/**',
|
||||||
|
'!content/images/**',
|
||||||
|
'content/images/README.md',
|
||||||
|
'!content/themes/**',
|
||||||
|
'content/themes/casper/**',
|
||||||
|
'!content/plugins/**',
|
||||||
|
'content/plugins/README.md',
|
||||||
'!node_modules/**',
|
'!node_modules/**',
|
||||||
|
'!core/test/**',
|
||||||
|
'!core/client/assets/sass/**',
|
||||||
'!**/*.db*',
|
'!**/*.db*',
|
||||||
'!*.db*',
|
'!*.db*',
|
||||||
'!.sass*',
|
'!.sass*',
|
||||||
|
@ -21,6 +30,8 @@ var path = require('path'),
|
||||||
'!.groc*',
|
'!.groc*',
|
||||||
'!*.iml',
|
'!*.iml',
|
||||||
'!config.js',
|
'!config.js',
|
||||||
|
'!CONTRIBUTING.md',
|
||||||
|
'!SECURITY.md',
|
||||||
'!.travis.yml'
|
'!.travis.yml'
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -40,7 +51,8 @@ var path = require('path'),
|
||||||
dist: distDirectory,
|
dist: distDirectory,
|
||||||
nightlyDist: path.join(distDirectory, 'nightly'),
|
nightlyDist: path.join(distDirectory, 'nightly'),
|
||||||
weeklyDist: path.join(distDirectory, 'weekly'),
|
weeklyDist: path.join(distDirectory, 'weekly'),
|
||||||
buildDist: path.join(distDirectory, 'build')
|
buildDist: path.join(distDirectory, 'build'),
|
||||||
|
releaseDist: path.join(distDirectory, 'release')
|
||||||
},
|
},
|
||||||
buildType: 'Build',
|
buildType: 'Build',
|
||||||
pkg: grunt.file.readJSON('package.json'),
|
pkg: grunt.file.readJSON('package.json'),
|
||||||
|
@ -340,6 +352,14 @@ var path = require('path'),
|
||||||
expand: true,
|
expand: true,
|
||||||
cwd: '<%= paths.buildBuild %>/',
|
cwd: '<%= paths.buildBuild %>/',
|
||||||
src: ['**']
|
src: ['**']
|
||||||
|
},
|
||||||
|
release: {
|
||||||
|
options: {
|
||||||
|
archive: '<%= paths.releaseDist %>/Ghost-<%= pkg.version %>.zip'
|
||||||
|
},
|
||||||
|
expand: true,
|
||||||
|
cwd: '<%= paths.buildBuild %>/',
|
||||||
|
src: ['**']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -813,6 +833,18 @@ var path = require('path'),
|
||||||
"compress:build"
|
"compress:build"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
grunt.registerTask('release', [
|
||||||
|
'shell:bourbon',
|
||||||
|
'sass:admin',
|
||||||
|
'handlebars',
|
||||||
|
'concat',
|
||||||
|
'uglify',
|
||||||
|
'changelog',
|
||||||
|
'clean:build',
|
||||||
|
'copy:build',
|
||||||
|
'compress:release'
|
||||||
|
]);
|
||||||
|
|
||||||
// Dev Mode; watch files and restart server on changes
|
// Dev Mode; watch files and restart server on changes
|
||||||
grunt.registerTask("dev", [
|
grunt.registerTask("dev", [
|
||||||
"sass:admin",
|
"sass:admin",
|
||||||
|
|
7
SECURITY.md
Normal file
7
SECURITY.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# Reporting Security Issues
|
||||||
|
|
||||||
|
If you discover a security issue in Ghost, please report it by sending an email to security[at]ghost[dot]org
|
||||||
|
|
||||||
|
This will allow us to assess the risk, and make a fix available before we add a bug report to the Github repo.
|
||||||
|
|
||||||
|
Thanks for helping make Ghost safe for everyone.
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
UploadUi = function ($dropzone, settings) {
|
UploadUi = function ($dropzone, settings) {
|
||||||
var source,
|
var source,
|
||||||
$url = '<div class="js-url"><input id="uploadurl" class="url" type="url" placeholder="http://"/></div>',
|
$url = '<div class="js-url"><input class="url js-upload-url" type="url" placeholder="http://"/></div>',
|
||||||
$cancel = '<a class="image-cancel js-cancel"><span class="hidden">Delete</span></a>',
|
$cancel = '<a class="image-cancel js-cancel"><span class="hidden">Delete</span></a>',
|
||||||
$progress = $('<div />', {
|
$progress = $('<div />', {
|
||||||
"class" : "js-upload-progress progress progress-success active",
|
"class" : "js-upload-progress progress progress-success active",
|
||||||
|
@ -57,7 +57,6 @@
|
||||||
}).attr('src', result);
|
}).attr('src', result);
|
||||||
}
|
}
|
||||||
preLoadImage();
|
preLoadImage();
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
bindFileUpload: function () {
|
bindFileUpload: function () {
|
||||||
|
@ -168,17 +167,21 @@
|
||||||
$dropzone.find('div.description').before($url);
|
$dropzone.find('div.description').before($url);
|
||||||
|
|
||||||
$dropzone.find('.js-button-accept').on('click', function () {
|
$dropzone.find('.js-button-accept').on('click', function () {
|
||||||
val = $('#uploadurl').val();
|
val = $dropzone.find('.js-upload-url').val();
|
||||||
$dropzone.trigger('uploadstart', [$dropzone.attr('id')]);
|
|
||||||
$dropzone.find('div.description').hide();
|
$dropzone.find('div.description').hide();
|
||||||
$dropzone.find('.js-fileupload').removeClass('right');
|
$dropzone.find('.js-fileupload').removeClass('right');
|
||||||
$dropzone.find('.js-url').remove();
|
$dropzone.find('.js-url').remove();
|
||||||
$dropzone.find('button.centre').remove();
|
$dropzone.find('button.centre').remove();
|
||||||
|
if (val === "") {
|
||||||
|
$dropzone.trigger("uploadsuccess", 'http://');
|
||||||
|
self.initWithDropzone();
|
||||||
|
} else {
|
||||||
self.complete(val);
|
self.complete(val);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
initWithImage: function () {
|
initWithImage: function () {
|
||||||
var self = this;
|
var self = this, val;
|
||||||
// This is the start point if an image already exists
|
// This is the start point if an image already exists
|
||||||
source = $dropzone.find('img.js-upload-target').attr('src');
|
source = $dropzone.find('img.js-upload-target').attr('src');
|
||||||
$dropzone.removeClass('image-uploader image-uploader-url').addClass('pre-image-uploader');
|
$dropzone.removeClass('image-uploader image-uploader-url').addClass('pre-image-uploader');
|
||||||
|
@ -187,6 +190,11 @@
|
||||||
$dropzone.find('.js-cancel').on('click', function () {
|
$dropzone.find('.js-cancel').on('click', function () {
|
||||||
$dropzone.find('img.js-upload-target').attr({'src': ''});
|
$dropzone.find('img.js-upload-target').attr({'src': ''});
|
||||||
$dropzone.find('div.description').show();
|
$dropzone.find('div.description').show();
|
||||||
|
$dropzone.delay(2500).animate({opacity: 100}, 1000, function () {
|
||||||
|
self.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
$dropzone.trigger("uploadsuccess", 'http://');
|
||||||
self.initWithDropzone();
|
self.initWithDropzone();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -443,10 +443,9 @@
|
||||||
var filestorage = $('#entry-markdown-content').data('filestorage');
|
var filestorage = $('#entry-markdown-content').data('filestorage');
|
||||||
this.$('.js-drop-zone').upload({editor: true, fileStorage: filestorage});
|
this.$('.js-drop-zone').upload({editor: true, fileStorage: filestorage});
|
||||||
this.$('.js-drop-zone').on('uploadstart', $.proxy(this.disableEditor, this));
|
this.$('.js-drop-zone').on('uploadstart', $.proxy(this.disableEditor, this));
|
||||||
this.$('.js-drop-zone').on('uploadstart', this.uploadMgr.handleDownloadStart);
|
|
||||||
this.$('.js-drop-zone').on('uploadfailure', $.proxy(this.enableEditor, this));
|
this.$('.js-drop-zone').on('uploadfailure', $.proxy(this.enableEditor, this));
|
||||||
this.$('.js-drop-zone').on('uploadsuccess', $.proxy(this.enableEditor, this));
|
this.$('.js-drop-zone').on('uploadsuccess', $.proxy(this.enableEditor, this));
|
||||||
this.$('.js-drop-zone').on('uploadsuccess', this.uploadMgr.handleDownloadSuccess);
|
this.$('.js-drop-zone').on('uploadsuccess', this.uploadMgr.handleUpload);
|
||||||
},
|
},
|
||||||
|
|
||||||
enableEditor: function () {
|
enableEditor: function () {
|
||||||
|
@ -611,7 +610,7 @@
|
||||||
// TODO: hasMarker but no image?
|
// TODO: hasMarker but no image?
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDownloadStart(e) {
|
function handleUpload(e, result_src) {
|
||||||
/*jslint regexp: true, bitwise: true */
|
/*jslint regexp: true, bitwise: true */
|
||||||
var line = findLine($(e.currentTarget).attr('id')),
|
var line = findLine($(e.currentTarget).attr('id')),
|
||||||
lineNumber = editor.getLineNumber(line),
|
lineNumber = editor.getLineNumber(line),
|
||||||
|
@ -636,9 +635,6 @@
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function handleDownloadSuccess(e, result_src) {
|
|
||||||
editor.replaceSelection(result_src);
|
editor.replaceSelection(result_src);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -655,8 +651,7 @@
|
||||||
// Public API
|
// Public API
|
||||||
_.extend(this, {
|
_.extend(this, {
|
||||||
getEditorValue: getEditorValue,
|
getEditorValue: getEditorValue,
|
||||||
handleDownloadStart: handleDownloadStart,
|
handleUpload: handleUpload
|
||||||
handleDownloadSuccess: handleDownloadSuccess
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// initialise
|
// initialise
|
||||||
|
|
|
@ -14,6 +14,7 @@ var express = require('express'),
|
||||||
hbs = require('express-hbs'),
|
hbs = require('express-hbs'),
|
||||||
Ghost = require('./ghost'),
|
Ghost = require('./ghost'),
|
||||||
helpers = require('./server/helpers'),
|
helpers = require('./server/helpers'),
|
||||||
|
middleware = require('./server/middleware'),
|
||||||
packageInfo = require('../package.json'),
|
packageInfo = require('../package.json'),
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
|
@ -193,7 +194,7 @@ function activateTheme() {
|
||||||
server.set('activeTheme', ghost.settings('activeTheme'));
|
server.set('activeTheme', ghost.settings('activeTheme'));
|
||||||
server.enable(server.get('activeTheme'));
|
server.enable(server.get('activeTheme'));
|
||||||
if (stackLocation) {
|
if (stackLocation) {
|
||||||
server.stack[stackLocation].handle = whenEnabled(server.get('activeTheme'), express['static'](ghost.paths().activeTheme));
|
server.stack[stackLocation].handle = whenEnabled(server.get('activeTheme'), middleware.staticTheme(ghost));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,7 +266,7 @@ when(ghost.init()).then(function () {
|
||||||
server.use('/ghost', whenEnabled('admin', express['static'](path.join(__dirname, '/client/assets'))));
|
server.use('/ghost', whenEnabled('admin', express['static'](path.join(__dirname, '/client/assets'))));
|
||||||
|
|
||||||
// Theme only config
|
// Theme only config
|
||||||
server.use(whenEnabled(server.get('activeTheme'), express['static'](ghost.paths().activeTheme)));
|
server.use(whenEnabled(server.get('activeTheme'), middleware.staticTheme(ghost)));
|
||||||
|
|
||||||
// Add in all trailing slashes
|
// Add in all trailing slashes
|
||||||
server.use(slashes());
|
server.use(slashes());
|
||||||
|
|
|
@ -95,7 +95,7 @@ GhostMailer.prototype.send = function (message) {
|
||||||
return when.reject(new Error('Email Error: Incomplete message data.'));
|
return when.reject(new Error('Email Error: Incomplete message data.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
var from = 'ghost-mailer@' + url.parse(this.ghost.config().url).hostname,
|
var from = this.ghost.config().mail.fromaddress || this.ghost.settings('email'),
|
||||||
to = message.to || this.ghost.settings('email'),
|
to = message.to || this.ghost.settings('email'),
|
||||||
sendMail = nodefn.lift(this.transport.sendMail.bind(this.transport));
|
sendMail = nodefn.lift(this.transport.sendMail.bind(this.transport));
|
||||||
|
|
||||||
|
|
31
core/server/middleware.js
Normal file
31
core/server/middleware.js
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
|
||||||
|
var _ = require('underscore'),
|
||||||
|
express = require('express'),
|
||||||
|
path = require('path');
|
||||||
|
|
||||||
|
function isBlackListedFileType(file) {
|
||||||
|
var blackListedFileTypes = ['.hbs', '.md', '.txt', '.json'],
|
||||||
|
ext = path.extname(file);
|
||||||
|
return _.contains(blackListedFileTypes, ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
var middleware = {
|
||||||
|
|
||||||
|
staticTheme: function (g) {
|
||||||
|
var ghost = g;
|
||||||
|
return function blackListStatic(req, res, next) {
|
||||||
|
if (isBlackListedFileType(req.url)) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return middleware.forwardToExpressStatic(ghost, req, res, next);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
// to allow unit testing
|
||||||
|
forwardToExpressStatic: function (ghost, req, res, next) {
|
||||||
|
return express['static'](ghost.paths().activeTheme)(req, res, next);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = middleware;
|
88
core/test/unit/middleware_spec.js
Normal file
88
core/test/unit/middleware_spec.js
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
/*globals describe, beforeEach, it*/
|
||||||
|
var assert = require('assert'),
|
||||||
|
should = require('should'),
|
||||||
|
sinon = require('sinon'),
|
||||||
|
when = require('when'),
|
||||||
|
express = require('express'),
|
||||||
|
middleware = require('../../server/middleware');
|
||||||
|
|
||||||
|
describe('Middleware', function () {
|
||||||
|
describe('staticTheme', function () {
|
||||||
|
var realExpressStatic = express.static;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
sinon.stub(middleware, 'forwardToExpressStatic').yields();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
middleware.forwardToExpressStatic.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call next if hbs file type', function (done) {
|
||||||
|
var req = {
|
||||||
|
url: 'mytemplate.hbs'
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.staticTheme(null)(req, null, function (a) {
|
||||||
|
should.not.exist(a);
|
||||||
|
middleware.forwardToExpressStatic.calledOnce.should.be.false;
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call next if md file type', function (done) {
|
||||||
|
var req = {
|
||||||
|
url: 'README.md'
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.staticTheme(null)(req, null, function (a) {
|
||||||
|
should.not.exist(a);
|
||||||
|
middleware.forwardToExpressStatic.calledOnce.should.be.false;
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call next if txt file type', function (done) {
|
||||||
|
var req = {
|
||||||
|
url: 'LICENSE.txt'
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.staticTheme(null)(req, null, function (a) {
|
||||||
|
should.not.exist(a);
|
||||||
|
middleware.forwardToExpressStatic.calledOnce.should.be.false;
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call next if json file type', function (done) {
|
||||||
|
var req = {
|
||||||
|
url: 'sample.json'
|
||||||
|
}
|
||||||
|
|
||||||
|
middleware.staticTheme(null)(req, null, function (a) {
|
||||||
|
should.not.exist(a);
|
||||||
|
middleware.forwardToExpressStatic.calledOnce.should.be.false;
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call express.static if valid file type', function (done) {
|
||||||
|
var ghostStub = {
|
||||||
|
paths: function() {
|
||||||
|
return {activeTheme: 'ACTIVETHEME'};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var req = {
|
||||||
|
url: 'myvalidfile.css'
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.staticTheme(ghostStub)(req, null, function (req, res, next) {
|
||||||
|
middleware.forwardToExpressStatic.calledOnce.should.be.true;
|
||||||
|
assert.deepEqual(middleware.forwardToExpressStatic.args[0][0], ghostStub);
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
11
package.json
11
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "ghost",
|
"name": "ghost",
|
||||||
"version": "0.3.1",
|
"version": "0.3.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node index",
|
"start": "node index",
|
||||||
|
@ -11,8 +11,8 @@
|
||||||
},
|
},
|
||||||
"engineStrict": true,
|
"engineStrict": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "3.4.0",
|
"express": "3.3.4",
|
||||||
"express-hbs": "0.3.0",
|
"express-hbs": "0.2.2",
|
||||||
"connect-slashes": "0.0.9",
|
"connect-slashes": "0.0.9",
|
||||||
"node-polyglot": "0.2.1",
|
"node-polyglot": "0.2.1",
|
||||||
"moment": "2.1.0",
|
"moment": "2.1.0",
|
||||||
|
@ -30,7 +30,10 @@
|
||||||
"downsize": "0.0.2",
|
"downsize": "0.0.2",
|
||||||
"validator": "1.4.0",
|
"validator": "1.4.0",
|
||||||
"rss": "0.2.0",
|
"rss": "0.2.0",
|
||||||
"nodemailer": "~0.5.2"
|
"nodemailer": "0.5.2"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"mysql": "2.0.0-alpha9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"grunt": "~0.4.1",
|
"grunt": "~0.4.1",
|
||||||
|
|
Loading…
Add table
Reference in a new issue