diff --git a/Gruntfile.js b/Gruntfile.js index b07bce3f8f..1ab9151cf9 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,13 +1,13 @@ -var path = require('path'), - when = require('when'), - semver = require("semver"), - fs = require("fs"), - path = require("path"), - _ = require('underscore'), - spawn = require("child_process").spawn, +var path = require('path'), + when = require('when'), + semver = require('semver'), + fs = require('fs'), + path = require('path'), + _ = require('underscore'), + spawn = require("child_process").spawn, buildDirectory = path.resolve(process.cwd(), '.build'), - distDirectory = path.resolve(process.cwd(), '.dist'), - configLoader = require('./core/config-loader.js'), + distDirectory = path.resolve(process.cwd(), '.dist'), + configLoader = require('./core/config-loader.js'), buildGlob = [ '**', @@ -94,7 +94,7 @@ var path = require('path'), // Start our server in development express: { options: { - script: "index.js" + script: 'index.js' }, dev: { @@ -136,9 +136,9 @@ var path = require('path'), }, files: { src: [ - "*.js", - "core/*.js", - "core/server/**/*.js" + '*.js', + 'core/*.js', + 'core/server/**/*.js' ] } }, @@ -156,11 +156,11 @@ var path = require('path'), unparam: true }, files: { - src: "core/client/**/*.js" + src: 'core/client/**/*.js' }, exclude: [ - "core/client/assets/**/*.js", - "core/client/tpl/**/*.js" + 'core/client/assets/**/*.js', + 'core/client/tpl/**/*.js' ] }, shared: { @@ -180,19 +180,19 @@ var path = require('path'), }, files: { src: [ - "core/shared/**/*.js" + 'core/shared/**/*.js' ] }, exclude: [ - "core/shared/vendor/**/*.js" + 'core/shared/vendor/**/*.js' ] } }, mochacli: { options: { - ui: "bdd", - reporter: "spec" + ui: 'bdd', + reporter: 'spec' }, all: { @@ -245,14 +245,14 @@ var path = require('path'), handlebars: { core: { options: { - namespace: "JST", + namespace: 'JST', processName: function (filename) { filename = filename.replace('core/client/tpl/', ''); return filename.replace('.hbs', ''); } }, files: { - "core/client/tpl/hbs-tpl.js": "core/client/tpl/**/*.hbs" + 'core/client/tpl/hbs-tpl.js': 'core/client/tpl/**/*.hbs' } } }, @@ -260,19 +260,19 @@ var path = require('path'), groc: { docs: { options: { - "out": "./docs/", - "glob": [ - "README.md", - "config.example.js", - "index.js", - "core/*.js", - "core/server/**/*.js", - "core/shared/**/*.js", - "core/client/**/*.js" + 'out': './docs/', + 'glob': [ + 'README.md', + 'config.example.js', + 'index.js', + 'core/*.js', + 'core/server/**/*.js', + 'core/shared/**/*.js', + 'core/client/**/*.js' ], - "except": [ - "!core/**/vendor/**/*.js", - "!core/client/tpl/**/*.js" + 'except': [ + '!core/**/vendor/**/*.js', + '!core/client/tpl/**/*.js' ] } } @@ -280,7 +280,7 @@ var path = require('path'), clean: { build: { - src: ["<%= paths.buildBuild %>/**"] + src: ['<%= paths.buildBuild %>/**'] } }, @@ -289,21 +289,21 @@ var path = require('path'), files: [{ expand: true, src: buildGlob, - dest: "<%= paths.nightlyBuild %>/<%= pkg.version %>/" + dest: '<%= paths.nightlyBuild %>/<%= pkg.version %>/' }] }, weekly: { files: [{ expand: true, src: buildGlob, - dest: "<%= paths.weeklyBuild %>/<%= pkg.version %>/" + dest: '<%= paths.weeklyBuild %>/<%= pkg.version %>/' }] }, build: { files: [{ expand: true, src: buildGlob, - dest: "<%= paths.buildBuild %>/" + dest: '<%= paths.buildBuild %>/' }] } }, @@ -311,27 +311,27 @@ var path = require('path'), compress: { nightly: { options: { - archive: "<%= paths.nightlyDist %>/Ghost-Nightly-<%= pkg.version %>.zip" + archive: '<%= paths.nightlyDist %>/Ghost-Nightly-<%= pkg.version %>.zip' }, expand: true, - cwd: "<%= paths.nightlyBuild %>/<%= pkg.version %>/", - src: ["**"] + cwd: '<%= paths.nightlyBuild %>/<%= pkg.version %>/', + src: ['**'] }, weekly: { options: { - archive: "<%= paths.weeklyDist %>/Ghost-Weekly-<%= pkg.version %>.zip" + archive: '<%= paths.weeklyDist %>/Ghost-Weekly-<%= pkg.version %>.zip' }, expand: true, - cwd: "<%= paths.weeklyBuild %>/<%= pkg.version %>/", - src: ["**"] + cwd: '<%= paths.weeklyBuild %>/<%= pkg.version %>/', + src: ['**'] }, build: { options: { - archive: "<%= paths.buildDist %>/Ghost-Build.zip" + archive: '<%= paths.buildDist %>/Ghost-Build.zip' }, expand: true, - cwd: "<%= paths.buildBuild %>/", - src: ["**"] + cwd: '<%= paths.buildBuild %>/', + src: ['**'] } }, @@ -340,113 +340,113 @@ var path = require('path'), tagName: '%VERSION%', commitMessage: '<%= buildType %> Release %VERSION%', tagMessage: '<%= buildType %> Release %VERSION%', - pushTo: "origin build" + pushTo: 'origin build' } }, concat: { dev: { files: { - "core/built/scripts/vendor.js": [ - "core/shared/vendor/jquery/jquery.js", - "core/shared/vendor/jquery/jquery-ui-1.10.3.custom.min.js", - "core/client/assets/lib/jquery-utils.js", - "core/client/assets/lib/uploader.js", - "core/shared/vendor/underscore.js", - "core/shared/vendor/backbone/backbone.js", - "core/shared/vendor/handlebars/handlebars-runtime.js", - "core/shared/vendor/moment.js", + 'core/built/scripts/vendor.js': [ + 'core/shared/vendor/jquery/jquery.js', + 'core/shared/vendor/jquery/jquery-ui-1.10.3.custom.min.js', + 'core/client/assets/lib/jquery-utils.js', + 'core/client/assets/lib/uploader.js', + 'core/shared/vendor/underscore.js', + 'core/shared/vendor/backbone/backbone.js', + 'core/shared/vendor/handlebars/handlebars-runtime.js', + 'core/shared/vendor/moment.js', - "core/client/assets/vendor/icheck/jquery.icheck.min.js", + 'core/client/assets/vendor/icheck/jquery.icheck.min.js', - "core/shared/vendor/jquery/jquery.ui.widget.js", - "core/shared/vendor/jquery/jquery.iframe-transport.js", - "core/shared/vendor/jquery/jquery.fileupload.js", + 'core/shared/vendor/jquery/jquery.ui.widget.js', + 'core/shared/vendor/jquery/jquery.iframe-transport.js', + 'core/shared/vendor/jquery/jquery.fileupload.js', - "core/client/assets/vendor/codemirror/codemirror.js", - "core/client/assets/vendor/codemirror/addon/mode/overlay.js", - "core/client/assets/vendor/codemirror/mode/markdown/markdown.js", - "core/client/assets/vendor/codemirror/mode/gfm/gfm.js", - "core/client/assets/vendor/showdown/showdown.js", - "core/client/assets/vendor/showdown/extensions/ghostdown.js", - "core/shared/vendor/showdown/extensions/github.js", - "core/client/assets/vendor/shortcuts.js", - "core/client/assets/vendor/validator-client.js", - "core/client/assets/vendor/countable.js", - "core/client/assets/vendor/to-title-case.js", - "core/client/assets/vendor/packery.pkgd.min.js", - "core/client/assets/vendor/fastclick.js" + 'core/client/assets/vendor/codemirror/codemirror.js', + 'core/client/assets/vendor/codemirror/addon/mode/overlay.js', + 'core/client/assets/vendor/codemirror/mode/markdown/markdown.js', + 'core/client/assets/vendor/codemirror/mode/gfm/gfm.js', + 'core/client/assets/vendor/showdown/showdown.js', + 'core/client/assets/vendor/showdown/extensions/ghostdown.js', + 'core/shared/vendor/showdown/extensions/github.js', + 'core/client/assets/vendor/shortcuts.js', + 'core/client/assets/vendor/validator-client.js', + 'core/client/assets/vendor/countable.js', + 'core/client/assets/vendor/to-title-case.js', + 'core/client/assets/vendor/packery.pkgd.min.js', + 'core/client/assets/vendor/fastclick.js' ], - "core/built/scripts/helpers.js": [ - "core/client/init.js", + 'core/built/scripts/helpers.js': [ + 'core/client/init.js', - "core/client/mobile-interactions.js", - "core/client/toggle.js", - "core/client/markdown-actions.js", - "core/client/helpers/index.js" + 'core/client/mobile-interactions.js', + 'core/client/toggle.js', + 'core/client/markdown-actions.js', + 'core/client/helpers/index.js' ], - "core/built/scripts/templates.js": [ - "core/client/tpl/hbs-tpl.js" + 'core/built/scripts/templates.js': [ + 'core/client/tpl/hbs-tpl.js' ], - "core/built/scripts/models.js": [ - "core/client/models/**/*.js" + 'core/built/scripts/models.js': [ + 'core/client/models/**/*.js' ], - "core/built/scripts/views.js": [ - "core/client/views/**/*.js", - "core/client/router.js" + 'core/built/scripts/views.js': [ + 'core/client/views/**/*.js', + 'core/client/router.js' ] } }, prod: { files: { - "core/built/scripts/ghost.js": [ - "core/shared/vendor/jquery/jquery.js", - "core/shared/vendor/jquery/jquery-ui-1.10.3.custom.min.js", - "core/client/assets/lib/jquery-utils.js", - "core/client/assets/lib/uploader.js", - "core/shared/vendor/underscore.js", - "core/shared/vendor/backbone/backbone.js", - "core/shared/vendor/handlebars/handlebars-runtime.js", - "core/shared/vendor/moment.js", + 'core/built/scripts/ghost.js': [ + 'core/shared/vendor/jquery/jquery.js', + 'core/shared/vendor/jquery/jquery-ui-1.10.3.custom.min.js', + 'core/client/assets/lib/jquery-utils.js', + 'core/client/assets/lib/uploader.js', + 'core/shared/vendor/underscore.js', + 'core/shared/vendor/backbone/backbone.js', + 'core/shared/vendor/handlebars/handlebars-runtime.js', + 'core/shared/vendor/moment.js', - "core/client/assets/vendor/icheck/jquery.icheck.min.js", + 'core/client/assets/vendor/icheck/jquery.icheck.min.js', - "core/shared/vendor/jquery/jquery.ui.widget.js", - "core/shared/vendor/jquery/jquery.iframe-transport.js", - "core/shared/vendor/jquery/jquery.fileupload.js", + 'core/shared/vendor/jquery/jquery.ui.widget.js', + 'core/shared/vendor/jquery/jquery.iframe-transport.js', + 'core/shared/vendor/jquery/jquery.fileupload.js', - "core/client/assets/vendor/codemirror/codemirror.js", - "core/client/assets/vendor/codemirror/addon/mode/overlay.js", - "core/client/assets/vendor/codemirror/mode/markdown/markdown.js", - "core/client/assets/vendor/codemirror/mode/gfm/gfm.js", - "core/client/assets/vendor/showdown/showdown.js", - "core/client/assets/vendor/showdown/extensions/ghostdown.js", - "core/shared/vendor/showdown/extensions/github.js", - "core/client/assets/vendor/shortcuts.js", - "core/client/assets/vendor/validator-client.js", - "core/client/assets/vendor/countable.js", - "core/client/assets/vendor/to-title-case.js", - "core/client/assets/vendor/packery.pkgd.min.js", - "core/client/assets/vendor/fastclick.js", + 'core/client/assets/vendor/codemirror/codemirror.js', + 'core/client/assets/vendor/codemirror/addon/mode/overlay.js', + 'core/client/assets/vendor/codemirror/mode/markdown/markdown.js', + 'core/client/assets/vendor/codemirror/mode/gfm/gfm.js', + 'core/client/assets/vendor/showdown/showdown.js', + 'core/client/assets/vendor/showdown/extensions/ghostdown.js', + 'core/shared/vendor/showdown/extensions/github.js', + 'core/client/assets/vendor/shortcuts.js', + 'core/client/assets/vendor/validator-client.js', + 'core/client/assets/vendor/countable.js', + 'core/client/assets/vendor/to-title-case.js', + 'core/client/assets/vendor/packery.pkgd.min.js', + 'core/client/assets/vendor/fastclick.js', - "core/client/init.js", + 'core/client/init.js', - "core/client/mobile-interactions.js", - "core/client/toggle.js", - "core/client/markdown-actions.js", - "core/client/helpers/index.js", + 'core/client/mobile-interactions.js', + 'core/client/toggle.js', + 'core/client/markdown-actions.js', + 'core/client/helpers/index.js', - "core/client/tpl/hbs-tpl.js", + 'core/client/tpl/hbs-tpl.js', - "core/client/models/**/*.js", + 'core/client/models/**/*.js', - "core/client/views/**/*.js", + 'core/client/views/**/*.js', - "core/client/router.js" + 'core/client/router.js' ] } } @@ -455,7 +455,7 @@ var path = require('path'), uglify: { prod: { files: { - "core/built/scripts/ghost.min.js": "core/built/scripts/ghost.js" + 'core/built/scripts/ghost.min.js': 'core/built/scripts/ghost.js' } } } @@ -526,17 +526,17 @@ var path = require('path'), depth = depth || 0; if (!depth) { - grunt.log.writeln("git " + args.join(" ")); + grunt.log.writeln('git ' + args.join(' ')); } var buffer = []; - spawn("git", args, { + spawn('git', args, { // We can reasonably assume the gruntfile will be in the root of the repo. cwd : __dirname, - stdio : ["ignore", "pipe", process.stderr] + stdio : ['ignore', 'pipe', process.stderr] - }).on("exit", function (code) { + }).on('exit', function (code) { // Process exited correctly but we got no output. // Spawn again, but make sure we don't spiral out of control. @@ -552,7 +552,7 @@ var path = require('path'), } if (code === 0) { - return callback(buffer.join("")); + return callback(buffer.join('')); } // We failed. Git returned a non-standard exit code. @@ -560,7 +560,7 @@ var path = require('path'), done(false); // Push returned data into the buffer - }).stdout.on("data", buffer.push.bind(buffer)); + }).stdout.on('data', buffer.push.bind(buffer)); } // Crazy function for getting around inconsistencies in tagging @@ -575,14 +575,14 @@ var path = require('path'), // into sort directly. Could be something to think about // in future. - if (semver.rcompare(a, "0.2.0") < 0 || - semver.rcompare(b, "0.2.0") < 0) { + if (semver.rcompare(a, '0.2.0') < 0 || + semver.rcompare(b, '0.2.0') < 0) { return semver.rcompare(a, b); } - a = a.split("-"); - b = b.split("-"); + a = a.split('-'); + b = b.split('-'); if (semver.rcompare(a[0], b[0]) !== 0) { return semver.rcompare(a[0], b[0]); @@ -597,7 +597,7 @@ var path = require('path'), // Gets tags in master branch, sorts them with semver, function getTags(callback) { - git(["show-ref", "--tags"], function (results) { + git(['show-ref', '--tags'], function (results) { results = results .split(/\n+/) .filter(function (tag) { @@ -605,8 +605,8 @@ var path = require('path'), }) .map(function (tag) { return { - "tag": tag.split(/tags\//).pop().trim(), - "ref": tag.split(/\s+/).shift().trim() + 'tag': tag.split(/tags\//).pop().trim(), + 'ref': tag.split(/\s+/).shift().trim() }; }) .sort(sortTags); @@ -620,11 +620,11 @@ var path = require('path'), var commits = [], commitRegex = new RegExp( - "\\n*[|\\*\\s]*commit\\s+([a-f0-9]+)" + - "\\n[|\\*\\s]*Author:\\s+([^<\\n]+)<([^>\\n]+)>" + - "\\n[|\\*\\s]*Date:\\s+([^\\n]+)" + - "\\n+[|\\*\\s]*[ ]{4}([^\\n]+)", - "ig" + '\\n*[|\\*\\s]*commit\\s+([a-f0-9]+)' + + '\\n[|\\*\\s]*Author:\\s+([^<\\n]+)<([^>\\n]+)>' + + '\\n[|\\*\\s]*Date:\\s+([^\\n]+)' + + '\\n+[|\\*\\s]*[ ]{4}([^\\n]+)', + 'ig' ); // Using String.prototype.replace as a kind of poor-man's substitute @@ -641,16 +641,16 @@ var path = require('path'), date = date.replace( /^(\w+)\s(\w+)\s(\d+)\s([\d\:]+)\s(\d+)\s([\+\-\d]+)$/, - "$1, $2 $3 $5 $4 $6" + '$1, $2 $3 $5 $4 $6' ); commits.push({ - "hash": hash, - "author": author, - "email": email, - "date": date, - "parsedDate": new Date(Date.parse(date)), - "message": message + 'hash': hash, + 'author': author, + 'email': email, + 'date': date, + 'parsedDate': new Date(Date.parse(date)), + 'message': message }); return null; @@ -662,8 +662,8 @@ var path = require('path'), // Gets git log for specified range. function getLog(to, from, callback) { - var range = from && to ? from + ".." + to : "", - args = [ "log", "master", "--no-color", "--no-merges", "--graph" ]; + var range = from && to ? from + '..' + to : '', + args = [ 'log', 'master', '--no-color', '--no-merges', '--graph' ]; if (range) { args.push(range); @@ -676,12 +676,12 @@ var path = require('path'), // Run the job getTags(function (tags) { - var logPath = path.join(__dirname, "CHANGELOG.md"), + var logPath = path.join(__dirname, 'CHANGELOG.md'), log = fs.createWriteStream(logPath), commitCache = {}; function processTag(tag, callback) { - var buffer = "", + var buffer = '', peek = tag[1]; tag = tag[0]; @@ -699,7 +699,7 @@ var path = require('path'), return callback(""); } - buffer += "## Release " + tag.tag + "\n"; + buffer += '## Release ' + tag.tag + '\n'; commits = commits .filter(function (commit) { @@ -707,12 +707,12 @@ var path = require('path'), // Get rid of jenkins' release tagging commits // Remove commits we've already spat out return ( - commit.author !== "TryGhost-Jenkins" && + commit.author !== 'TryGhost-Jenkins' && !commitCache[commit.hash] ); }) .map(function (commit) { - buffer += "\n* " + commit.message + " (_" + commit.author + "_)"; + buffer += '\n* ' + commit.message + ' (_' + commit.author + '_)'; commitCache[commit.hash] = true; }); @@ -720,12 +720,12 @@ var path = require('path'), buffer += "\nNo changes were made in this build.\n"; } - callback(buffer + "\n"); + callback(buffer + '\n'); }); } // Get two weeks' worth of tags - tags.unshift({"tag": "HEAD"}); + tags.unshift({'tag': 'HEAD'}); tags = tags @@ -737,17 +737,17 @@ var path = require('path'), ]; }); - log.write("# Ghost Changelog\n\n"); - log.write("_Showing " + tags.length + " releases._\n"); + log.write('# Ghost Changelog\n\n'); + log.write('_Showing ' + tags.length + ' releases._\n'); when.reduce(tags, function (prev, tag, idx) { return when.promise(function (resolve) { processTag(tag, function (releaseData) { - resolve(prev + "\n" + releaseData); + resolve(prev + '\n' + releaseData); }); }); - }, "") + }, '') .then(function (reducedChangelog) { log.write(reducedChangelog); log.close(); @@ -765,75 +765,75 @@ var path = require('path'), * - Zip files in build folder to dist-folder/#{version} directory */ grunt.registerTask("nightly", [ - "setCurrentBuildType:Nightly", - "shell:bourbon", - "sass:admin", - "handlebars", - "concat", - "uglify", - "bump:build", - "updateCurrentPackageInfo", - "changelog", - "copy:nightly", - "compress:nightly" + 'setCurrentBuildType:Nightly', + 'shell:bourbon', + 'sass:admin', + 'handlebars', + 'concat', + 'uglify', + 'bump:build', + 'updateCurrentPackageInfo', + 'changelog', + 'copy:nightly', + 'compress:nightly' ]); grunt.registerTask("weekly", [ - "setCurrentBuildType:Weekly", - "shell:bourbon", - "sass:admin", - "handlebars", - "concat", - "uglify", - "bump:build", - "updateCurrentPackageInfo", - "changelog", - "copy:weekly", - "compress:weekly" + 'setCurrentBuildType:Weekly', + 'shell:bourbon', + 'sass:admin', + 'handlebars', + 'concat', + 'uglify', + 'bump:build', + 'updateCurrentPackageInfo', + 'changelog', + 'copy:weekly', + 'compress:weekly' ]); - grunt.registerTask("build", [ - "shell:bourbon", - "sass:admin", - "handlebars", - "concat", - "uglify", - "changelog", - "clean:build", - "copy:build", - "compress:build" + grunt.registerTask('build', [ + 'shell:bourbon', + 'sass:admin', + 'handlebars', + 'concat', + 'uglify', + 'changelog', + 'clean:build', + 'copy:build', + 'compress:build' ]); // Dev Mode; watch files and restart server on changes - grunt.registerTask("dev", [ - "default", - "express:dev", - "open", - "watch" + grunt.registerTask('dev', [ + 'default', + 'express:dev', + 'open', + 'watch' ]); // Prepare the project for development // TODO: Git submodule init/update (https://github.com/jaubourg/grunt-update-submodules)? - grunt.registerTask("init", ["shell:bourbon", "default"]); + grunt.registerTask('init', ['shell:bourbon', 'default']); // Run unit tests - grunt.registerTask("test-unit", ['setTestEnv', 'loadConfig', "mochacli:all"]); + grunt.registerTask('test-unit', ['setTestEnv', 'loadConfig', 'mochacli:all']); // Run casperjs tests only grunt.registerTask('test-functional', ['setTestEnv', 'express:test', 'spawn-casperjs']); // Run tests and lint code - grunt.registerTask("validate", ["jslint", "test-unit", "test-functional"]); + grunt.registerTask('validate', ['jslint', 'test-unit', 'test-functional']); // Generate Docs - grunt.registerTask("docs", ["groc"]); + grunt.registerTask('docs', ['groc']); // TODO: Production build task that minifies with uglify:prod - grunt.registerTask("prod", ['sass:admin', 'handlebars', 'concat', "uglify"]); + grunt.registerTask('prod', ['sass:admin', 'handlebars', 'concat', 'uglify']); - // When you just say "grunt" - grunt.registerTask("default", ['sass:admin', 'handlebars', 'concat']); + // When you just say 'grunt' + grunt.registerTask('default', ['sass:admin', 'handlebars', 'concat']); }; module.exports = configureGrunt; diff --git a/config.example.js b/config.example.js index bfdb4b1555..ac5e35dc18 100644 --- a/config.example.js +++ b/config.example.js @@ -34,7 +34,9 @@ config = { debug: false }, server: { + // Host to be passed to node's `net.Server#listen()` host: '127.0.0.1', + // Port to be passed to node's `net.Server#listen()`, for iisnode set this to `process.env.PORT` port: '2368' } }, @@ -53,7 +55,9 @@ config = { debug: false }, server: { + // Host to be passed to node's `net.Server#listen()` host: '127.0.0.1', + // Port to be passed to node's `net.Server#listen()`, for iisnode set this to `process.env.PORT` port: '2368' } }, diff --git a/content/themes/casper b/content/themes/casper index 2bd73d696a..c4c276653d 160000 --- a/content/themes/casper +++ b/content/themes/casper @@ -1 +1 @@ -Subproject commit 2bd73d696a53671afe1de0fd5a59a4c22e86b740 +Subproject commit c4c276653dc751e50fd927bd8c88a72930f4beff diff --git a/core/client/assets/lib/uploader.js b/core/client/assets/lib/uploader.js index f8d45e9c82..28b8ce93ff 100644 --- a/core/client/assets/lib/uploader.js +++ b/core/client/assets/lib/uploader.js @@ -23,8 +23,6 @@ complete: function (result) { var self = this; - $dropzone.trigger("uploadsuccess", [result, $dropzone.attr('id')]); - function showImage(width, height) { $dropzone.find('img.js-upload-target').attr({"width": width, "height": height}).css({"display": "block"}); $dropzone.find('.fileupload-loading').remove(); @@ -137,7 +135,7 @@ var self = this; //This is the start point if no image exists $dropzone.find('img.js-upload-target').css({"display": "none"}); - $dropzone.removeClass('pre-image-uploader').addClass('image-uploader'); + $dropzone.removeClass('pre-image-uploader image-uploader-url').addClass('image-uploader'); this.removeExtras(); this.buildExtras(); this.bindFileUpload(); @@ -148,6 +146,7 @@ initUrl: function () { var self = this, val; this.removeExtras(); + $dropzone.addClass('image-uploader-url').removeClass('pre-image-uploader'); $dropzone.find('.js-fileupload').addClass('right'); $dropzone.append($cancel); $dropzone.find('.js-cancel').on('click', function () { @@ -163,6 +162,7 @@ $dropzone.find('div.description').before($url); $dropzone.find('.js-button-accept').on('click', function () { + $dropzone.trigger('uploadstart', [$dropzone.attr('id')]); $dropzone.find('div.description').hide(); val = $('#uploadurl').val(); $dropzone.find('.js-fileupload').removeClass('right'); @@ -175,7 +175,7 @@ var self = this; // This is the start point if an image already exists source = $dropzone.find('img.js-upload-target').attr('src'); - $dropzone.removeClass('image-uploader').addClass('pre-image-uploader'); + $dropzone.removeClass('image-uploader image-uploader-url').addClass('pre-image-uploader'); $dropzone.find('div.description').hide(); $dropzone.append($cancel); $dropzone.find('.js-cancel').on('click', function () { diff --git a/core/client/assets/sass/modules/global.scss b/core/client/assets/sass/modules/global.scss index 6145ea0f7f..0936b5c10b 100644 --- a/core/client/assets/sass/modules/global.scss +++ b/core/client/assets/sass/modules/global.scss @@ -1277,7 +1277,8 @@ main { .centre{ position: relative; top: 50px; - margin-bottom: -6px; + display: block; + margin: 0 auto -6px auto; } .media { @include icon($i-image, 60px, darken($lightbrown, 3%)) { @@ -1408,6 +1409,7 @@ main { color: $brown; background: rgba(0,0,0,0.1); border-radius: 2px; + min-height: 46px; input { position: absolute; diff --git a/core/client/helpers/index.js b/core/client/helpers/index.js index 4ecf2cdc8b..cacaf9c7a6 100644 --- a/core/client/helpers/index.js +++ b/core/client/helpers/index.js @@ -1,9 +1,9 @@ /*globals Handlebars, moment */ (function () { - "use strict"; + 'use strict'; Handlebars.registerHelper('date', function (context, block) { - var f = block.hash.format || "MMM Do, YYYY", + var f = block.hash.format || 'MMM Do, YYYY', timeago = block.hash.timeago, date; if (timeago) { diff --git a/core/client/init.js b/core/client/init.js index 62a444b444..77ca1043e9 100644 --- a/core/client/init.js +++ b/core/client/init.js @@ -1,6 +1,6 @@ /*globals window, $, _, Backbone, Validator */ (function () { - "use strict"; + 'use strict'; var Ghost = { Layout : {}, diff --git a/core/client/markdown-actions.js b/core/client/markdown-actions.js index c3bb14fac1..ef3d72ae41 100644 --- a/core/client/markdown-actions.js +++ b/core/client/markdown-actions.js @@ -2,7 +2,7 @@ /*global $, window, CodeMirror, Showdown, moment */ (function () { - "use strict"; + 'use strict'; var Markdown = { init : function (options, elem) { var self = this; @@ -17,59 +17,59 @@ replace: function () { var text = this.elem.getSelection(), pass = true, md, cursor, line, word, letterCount, converter; switch (this.style) { - case "h1": + case 'h1': cursor = this.elem.getCursor(); line = this.elem.getLine(cursor.line); - this.elem.setLine(cursor.line, "# " + line); + this.elem.setLine(cursor.line, '# ' + line); this.elem.setCursor(cursor.line, cursor.ch + 2); pass = false; break; - case "h2": + case 'h2': cursor = this.elem.getCursor(); line = this.elem.getLine(cursor.line); - this.elem.setLine(cursor.line, "## " + line); + this.elem.setLine(cursor.line, '## ' + line); this.elem.setCursor(cursor.line, cursor.ch + 3); pass = false; break; - case "h3": + case 'h3': cursor = this.elem.getCursor(); line = this.elem.getLine(cursor.line); - this.elem.setLine(cursor.line, "### " + line); + this.elem.setLine(cursor.line, '### ' + line); this.elem.setCursor(cursor.line, cursor.ch + 4); pass = false; break; - case "h4": + case 'h4': cursor = this.elem.getCursor(); line = this.elem.getLine(cursor.line); - this.elem.setLine(cursor.line, "#### " + line); + this.elem.setLine(cursor.line, '#### ' + line); this.elem.setCursor(cursor.line, cursor.ch + 5); pass = false; break; - case "h5": + case 'h5': cursor = this.elem.getCursor(); line = this.elem.getLine(cursor.line); - this.elem.setLine(cursor.line, "##### " + line); + this.elem.setLine(cursor.line, '##### ' + line); this.elem.setCursor(cursor.line, cursor.ch + 6); pass = false; break; - case "h6": + case 'h6': cursor = this.elem.getCursor(); line = this.elem.getLine(cursor.line); - this.elem.setLine(cursor.line, "###### " + line); + this.elem.setLine(cursor.line, '###### ' + line); this.elem.setCursor(cursor.line, cursor.ch + 7); pass = false; break; - case "link": + case 'link': md = this.options.syntax.link.replace('$1', text); - this.elem.replaceSelection(md, "end"); + this.elem.replaceSelection(md, 'end'); cursor = this.elem.getCursor(); this.elem.setSelection({line: cursor.line, ch: cursor.ch - 8}, {line: cursor.line, ch: cursor.ch - 1}); pass = false; break; - case "image": + case 'image': cursor = this.elem.getCursor(); md = this.options.syntax.image.replace('$1', text); - if (this.elem.getLine(cursor.line) !== "") { + if (this.elem.getLine(cursor.line) !== '') { md = "\n\n" + md; } this.elem.replaceSelection(md, "end"); @@ -77,16 +77,16 @@ this.elem.setSelection({line: cursor.line, ch: cursor.ch - 8}, {line: cursor.line, ch: cursor.ch - 1}); pass = false; break; - case "uppercase": + case 'uppercase': md = text.toLocaleUpperCase(); break; - case "lowercase": + case 'lowercase': md = text.toLocaleLowerCase(); break; - case "titlecase": + case 'titlecase': md = text.toTitleCase(); break; - case "selectword": + case 'selectword': cursor = this.elem.getCursor(); word = this.elem.getTokenAt(cursor); if (!/\w$/g.test(word.string)) { @@ -95,7 +95,7 @@ this.elem.setSelection({line: cursor.line, ch: word.start}, {line: cursor.line, ch: word.end}); } break; - case "copyHTML": + case 'copyHTML': converter = new Showdown.converter(); if (text) { md = converter.makeHtml(text); @@ -106,13 +106,15 @@ $(".modal-copyToHTML-content").text(md).selectText(); pass = false; break; - case "list": - md = text.replace(/^(\s*)(\w\W*)/gm, "$1* $2"); - this.elem.replaceSelection(md, "end"); + case 'list': + md = text.replace(/^(\s*)(\w\W*)/gm, '$1* $2'); + this.elem.replaceSelection(md, 'end'); pass = false; break; - case "currentDate": - md = moment(new Date()).format("D MMMM YYYY"); + case 'currentDate': + md = moment(new Date()).format('D MMMM YYYY'); + this.elem.replaceSelection(md, 'end'); + pass = false; break; default: if (this.options.syntax[this.style]) { @@ -120,7 +122,7 @@ } } if (pass && md) { - this.elem.replaceSelection(md, "end"); + this.elem.replaceSelection(md, 'end'); if (!text) { letterCount = md.length; cursor = this.elem.getCursor(); diff --git a/core/client/mobile-interactions.js b/core/client/mobile-interactions.js index d731b532ec..2509abc81c 100644 --- a/core/client/mobile-interactions.js +++ b/core/client/mobile-interactions.js @@ -3,49 +3,49 @@ /*global window, document, $, FastClick */ (function () { - "use strict"; + 'use strict'; FastClick.attach(document.body); // ### Show content preview when swiping left on content list - $(".manage").on("click", ".content-list ol li", function (event) { + $('.manage').on('click', '.content-list ol li', function (event) { if (window.matchMedia('(max-width: 800px)').matches) { event.preventDefault(); event.stopPropagation(); - $(".content-list").animate({right: "100%", left: "-100%", 'margin-right': "15px"}, 300); - $(".content-preview").animate({right: "0", left: "0", 'margin-left': "0"}, 300); + $('.content-list').animate({right: '100%', left: '-100%', 'margin-right': '15px'}, 300); + $('.content-preview').animate({right: '0', left: '0', 'margin-left': '0'}, 300); } }); // ### Hide content preview - $(".manage").on("click", ".content-preview .button-back", function (event) { + $('.manage').on('click', '.content-preview .button-back', function (event) { if (window.matchMedia('(max-width: 800px)').matches) { event.preventDefault(); event.stopPropagation(); - $(".content-list").animate({right: "0", left: "0", 'margin-right': "0"}, 300); - $(".content-preview").animate({right: "-100%", left: "100%", 'margin-left': "15px"}, 300); + $('.content-list').animate({right: '0', left: '0', 'margin-right': '0'}, 300); + $('.content-preview').animate({right: '-100%', left: '100%', 'margin-left': '15px'}, 300); } }); // ### Show settings options page when swiping left on settings menu link - $(".settings").on("click", ".settings-menu li", function (event) { + $('.settings').on('click', '.settings-menu li', function (event) { if (window.matchMedia('(max-width: 800px)').matches) { event.preventDefault(); event.stopPropagation(); - $(".settings-sidebar").animate({right: "100%", left: "-102%", 'margin-right': "15px"}, 300); - $(".settings-content").animate({right: "0", left: "0", 'margin-left': "0"}, 300); - $(".settings-content .button-back, .settings-content .button-save").css("display", "inline-block"); + $('.settings-sidebar').animate({right: '100%', left: '-102%', 'margin-right': '15px'}, 300); + $('.settings-content').animate({right: '0', left: '0', 'margin-left': '0'}, 300); + $('.settings-content .button-back, .settings-content .button-save').css('display', 'inline-block'); } }); // ### Hide settings options page - $(".settings").on("click", ".settings-content .button-back", function (event) { + $('.settings').on('click', '.settings-content .button-back', function (event) { if (window.matchMedia('(max-width: 800px)').matches) { event.preventDefault(); event.stopPropagation(); - $(".settings-sidebar").animate({right: "0", left: "0", 'margin-right': "0"}, 300); - $(".settings-content").animate({right: "-100%", left: "100%", 'margin-left': "15"}, 300); - $(".settings-content .button-back, .settings-content .button-save").css("display", "none"); + $('.settings-sidebar').animate({right: '0', left: '0', 'margin-right': '0'}, 300); + $('.settings-content').animate({right: '-100%', left: '100%', 'margin-left': '15'}, 300); + $('.settings-content .button-back, .settings-content .button-save').css('display', 'none'); } }); diff --git a/core/client/models/post.js b/core/client/models/post.js index 0d37c4538f..360a3420ff 100644 --- a/core/client/models/post.js +++ b/core/client/models/post.js @@ -1,6 +1,6 @@ /*global window, document, Ghost, $, _, Backbone */ (function () { - "use strict"; + 'use strict'; Ghost.Models.Post = Backbone.Model.extend({ @@ -12,8 +12,8 @@ parse: function (resp) { if (resp.status) { - resp.published = !!(resp.status === "published"); - resp.draft = !!(resp.status === "draft"); + resp.published = !!(resp.status === 'published'); + resp.draft = !!(resp.status === 'draft'); } if (resp.tags) { // TODO: parse tags into it's own collection on the model (this.tags) diff --git a/core/client/models/settings.js b/core/client/models/settings.js index f6b926ae0a..1d0e9a38b7 100644 --- a/core/client/models/settings.js +++ b/core/client/models/settings.js @@ -1,10 +1,10 @@ /*global window, document, Ghost, $, _, Backbone */ (function () { - "use strict"; + 'use strict'; //id:0 is used to issue PUT requests Ghost.Models.Settings = Backbone.Model.extend({ url: Ghost.settings.apiRoot + '/settings/?type=blog,theme', - id: "0" + id: '0' }); }()); \ No newline at end of file diff --git a/core/client/models/tag.js b/core/client/models/tag.js index b3be4b6d23..444e3526d4 100644 --- a/core/client/models/tag.js +++ b/core/client/models/tag.js @@ -1,6 +1,6 @@ /*global window, document, Ghost, $, _, Backbone */ (function () { - "use strict"; + 'use strict'; Ghost.Collections.Tags = Backbone.Collection.extend({ url: Ghost.settings.apiRoot + '/tags/' diff --git a/core/client/models/themes.js b/core/client/models/themes.js index 47549b758d..5e82d77201 100644 --- a/core/client/models/themes.js +++ b/core/client/models/themes.js @@ -1,6 +1,6 @@ /*global window, document, Ghost, $, _, Backbone */ (function () { - "use strict"; + 'use strict'; Ghost.Models.Themes = Backbone.Model.extend({ url: Ghost.settings.apiRoot + '/themes' diff --git a/core/client/models/uploadModal.js b/core/client/models/uploadModal.js index 36398b1ab9..2c8aabb9ea 100644 --- a/core/client/models/uploadModal.js +++ b/core/client/models/uploadModal.js @@ -1,11 +1,11 @@ /*global Ghost, Backbone */ (function () { - "use strict"; + 'use strict'; Ghost.Models.uploadModal = Backbone.Model.extend({ options: { close: true, - type: "action", + type: 'action', style: ["wide"], animation: 'fade', afterRender: function () { diff --git a/core/client/models/user.js b/core/client/models/user.js index d4317e1516..7f30a4f895 100644 --- a/core/client/models/user.js +++ b/core/client/models/user.js @@ -1,6 +1,6 @@ /*global window, document, Ghost, $, _, Backbone */ (function () { - "use strict"; + 'use strict'; Ghost.Models.User = Backbone.Model.extend({ url: Ghost.settings.apiRoot + '/users/me/' diff --git a/core/client/models/widget.js b/core/client/models/widget.js index e2f7ae5350..53c0977f31 100644 --- a/core/client/models/widget.js +++ b/core/client/models/widget.js @@ -1,15 +1,15 @@ /*global window, document, Ghost, $, _, Backbone */ (function () { - "use strict"; + 'use strict'; Ghost.Models.Widget = Backbone.Model.extend({ defaults: { - title: "", - name: "", - author: "", - applicationID: "", - size: "", + title: '', + name: '', + author: '', + applicationID: '', + size: '', content: { template: '', data: { @@ -17,9 +17,9 @@ count: 0, sub: { value: 0, - dir: "", // "up" or "down" - item: "", - period: "" + dir: '', // "up" or "down" + item: '', + period: '' } } } @@ -28,8 +28,8 @@ settingsPane: false, enabled: false, options: [{ - title: "ERROR", - value: "Widget options not set" + title: 'ERROR', + value: 'Widget options not set' }] } } diff --git a/core/client/toggle.js b/core/client/toggle.js index 87bfd345cc..8fd9a14aea 100644 --- a/core/client/toggle.js +++ b/core/client/toggle.js @@ -2,7 +2,7 @@ /*global document, $, Ghost */ (function () { - "use strict"; + 'use strict'; Ghost.temporary.hideToggles = function () { $('[data-toggle]').each(function () { @@ -11,7 +11,7 @@ }); // Toggle active classes on menu headers - $("[data-toggle].active").removeClass("active"); + $('[data-toggle].active').removeClass('active'); }; Ghost.temporary.initToggles = function ($el) { diff --git a/core/client/views/editor.js b/core/client/views/editor.js index 904231bf47..c8802b5bc5 100644 --- a/core/client/views/editor.js +++ b/core/client/views/editor.js @@ -281,7 +281,7 @@ this.addSubview(new PublishBar({el: "#publish-bar", model: this.model})).render(); this.$('#entry-title').val(this.model.get('title')).focus(); - this.$('#entry-markdown').html(this.model.get('markdown')); + this.$('#entry-markdown').text(this.model.get('markdown')); this.initMarkdown(); this.renderPreview(); diff --git a/core/config-loader.js b/core/config-loader.js index ff9e8ec167..b22c77a1e7 100644 --- a/core/config-loader.js +++ b/core/config-loader.js @@ -1,5 +1,7 @@ -var fs = require('fs'), - when = require('when'); +var fs = require('fs'), + url = require('url'), + when = require('when'), + errors = require('./server/errorHandling'); function writeConfigFile() { var written = when.defer(); @@ -11,19 +13,19 @@ function writeConfigFile() { write; if (!templateExists) { - throw new Error('Could not locate a configuration file. Please check your deployment for config.js or config.example.js.'); + return errors.logError(new Error('Could not locate a configuration file.'), process.cwd(), 'Please check your deployment for config.js or config.example.js.'); } // Copy config.example.js => config.js read = fs.createReadStream('config.example.js'); read.on('error', function (err) { - throw new Error('Could not open config.example.js for read.'); + return errors.logError(new Error('Could not open config.example.js for read.'), process.cwd(), 'Please check your deployment for config.js or config.example.js.'); }); read.on('end', written.resolve); write = fs.createWriteStream('config.js'); write.on('error', function (err) { - throw new Error('Could not open config.js for write.'); + return errors.logError(new Error('Could not open config.js for write.'), process.cwd(), 'Please check your deployment for config.js or config.example.js.'); }); read.pipe(write); @@ -32,15 +34,54 @@ function writeConfigFile() { return written.promise; } +function validateConfigEnvironment() { + var envVal = process.env.NODE_ENV || 'undefined', + config, + parsedUrl; + + try { + config = require('../config')[envVal]; + } catch (ignore) { + + } + + // 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'); + return when.reject(); + } + + // Check that our url is valid + parsedUrl = url.parse(config.url || 'invalid'); + if (!parsedUrl.protocol || !parsedUrl.host) { + errors.logError(new Error('Your site url in config.js is invalid.'), config.url, 'Please make sure this is a valid url before restarting'); + return when.reject(); + } + + // Check that we have database values + if (!config.database) { + errors.logError(new Error('Your database configuration in config.js is invalid.'), JSON.stringify(config.database), 'Please make sure this is a valid Bookshelf database configuration'); + return when.reject(); + } + + // 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.'); + return when.reject(); + } + + return when.resolve(); +} + exports.loadConfig = function () { var loaded = when.defer(); /* Check for config file and copy from config.example.js if one doesn't exist. After that, start the server. */ fs.exists('config.js', function checkConfig(configExists) { if (configExists) { - loaded.resolve(); + validateConfigEnvironment().then(loaded.resolve).otherwise(loaded.reject); } else { - writeConfigFile().then(loaded.resolve).otherwise(loaded.reject); + writeConfigFile().then(validateConfigEnvironment).then(loaded.resolve).otherwise(loaded.reject); } }); return loaded.promise; diff --git a/core/ghost.js b/core/ghost.js index 5df4119e11..86735d25e5 100644 --- a/core/ghost.js +++ b/core/ghost.js @@ -2,28 +2,28 @@ // Defines core methods required to build the application // Module dependencies -var config = require('../config'), - when = require('when'), - express = require('express'), - errors = require('./server/errorHandling'), - fs = require('fs'), - path = require('path'), - hbs = require('express-hbs'), - nodefn = require('when/node/function'), - _ = require('underscore'), - Polyglot = require('node-polyglot'), - Mailer = require('./server/mail'), - models = require('./server/models'), - plugins = require('./server/plugins'), +var config = require('../config'), + when = require('when'), + express = require('express'), + errors = require('./server/errorHandling'), + fs = require('fs'), + path = require('path'), + hbs = require('express-hbs'), + nodefn = require('when/node/function'), + _ = require('underscore'), + Polyglot = require('node-polyglot'), + Mailer = require('./server/mail'), + models = require('./server/models'), + plugins = require('./server/plugins'), requireTree = require('./server/require-tree'), permissions = require('./server/permissions'), - uuid = require('node-uuid'), + uuid = require('node-uuid'), // Variables - appRoot = path.resolve(__dirname, '../'), - themePath = path.resolve(appRoot + '/content/themes'), - pluginPath = path.resolve(appRoot + '/content/plugins'), - themeDirectories = requireTree(themePath), + appRoot = path.resolve(__dirname, '../'), + themePath = path.resolve(appRoot + '/content/themes'), + pluginPath = path.resolve(appRoot + '/content/plugins'), + themeDirectories = requireTree(themePath), pluginDirectories = requireTree(pluginPath), Ghost, @@ -111,7 +111,7 @@ Ghost = function () { 'appRoot': appRoot, 'themePath': themePath, 'pluginPath': pluginPath, - 'activeTheme': path.join(themePath, !instance.settingsCache ? "" : instance.settingsCache.activeTheme.value), + 'activeTheme': path.join(themePath, !instance.settingsCache ? '' : instance.settingsCache.activeTheme.value), 'adminViews': path.join(appRoot, '/core/server/views/'), 'helperTemplates': path.join(appRoot, '/core/server/helpers/tpl/'), 'lang': path.join(appRoot, '/core/shared/lang/'), @@ -130,14 +130,14 @@ Ghost.prototype.init = function () { function doFirstRun() { var firstRunMessage = [ - "Welcome to Ghost.", - "You're running under the ", + 'Welcome to Ghost.', + 'You\'re running under the ', process.env.NODE_ENV, - "environment.", + 'environment.', - "Your URL is set to", - "" + self.config().url + ".", - "See http://docs.ghost.org for instructions." + 'Your URL is set to', + '' + self.config().url + '.', + 'See http://docs.ghost.org for instructions.' ]; self.notifications.push({ @@ -165,14 +165,13 @@ Ghost.prototype.init = function () { } // ### Initialisation - // make sure things are done in order return when.join( // Initialise the models - instance.dataProvider.init(), + self.dataProvider.init(), // Calculate paths - instance.getPaths(), + self.getPaths(), // Initialise mail after first run - instance.mail.init(self) + self.mail.init(self) ).then(function () { // Populate any missing default settings return models.Settings.populateDefaults(); @@ -183,7 +182,7 @@ Ghost.prototype.init = function () { return when.join( // Check for or initialise a dbHash. initDbHashAndFirstRun(), - // Initialize plugins + // Initialize plugins self.initPlugins(), // Initialize the permissions actions and objects permissions.init() @@ -261,7 +260,7 @@ Ghost.prototype.loadTemplate = function (name) { var self = this, templateFileName = name + '.hbs', // Check for theme specific version first - templatePath = path.join(this.paths().activeTheme, "partials", templateFileName), + templatePath = path.join(this.paths().activeTheme, 'partials', templateFileName), deferred = when.defer(); // Can't use nodefn here because exists just returns one parameter, true or false diff --git a/core/server.js b/core/server.js index f204436b9c..4f752425c5 100644 --- a/core/server.js +++ b/core/server.js @@ -1,18 +1,18 @@ // Module dependencies -var express = require('express'), - when = require('when'), - _ = require('underscore'), - colors = require("colors"), - semver = require("semver"), - slashes = require("connect-slashes"), - errors = require('./server/errorHandling'), - admin = require('./server/controllers/admin'), - frontend = require('./server/controllers/frontend'), - api = require('./server/api'), - path = require('path'), - hbs = require('express-hbs'), - Ghost = require('./ghost'), - helpers = require('./server/helpers'), +var express = require('express'), + when = require('when'), + _ = require('underscore'), + colors = require('colors'), + semver = require('semver'), + slashes = require('connect-slashes'), + errors = require('./server/errorHandling'), + admin = require('./server/controllers/admin'), + frontend = require('./server/controllers/frontend'), + api = require('./server/api'), + path = require('path'), + hbs = require('express-hbs'), + Ghost = require('./ghost'), + helpers = require('./server/helpers'), packageInfo = require('../package.json'), // Variables @@ -112,7 +112,8 @@ function ghostLocals(req, res, next) { _.extend(res.locals, { currentUser: { name: currentUser.attributes.name, - profile: currentUser.attributes.image + email: currentUser.attributes.email, + image: currentUser.attributes.image } }); next(); @@ -128,8 +129,8 @@ function ghostLocals(req, res, next) { // Disable any caching until it can be done properly function disableCachedResult(req, res, next) { res.set({ - "Cache-Control": "no-cache, must-revalidate", - "Expires": "Sat, 26 Jul 1997 05:00:00 GMT" + 'Cache-Control': 'no-cache, must-revalidate', + 'Expires': 'Sat, 26 Jul 1997 05:00:00 GMT' }); next(); @@ -222,13 +223,15 @@ function manageAdminAndTheme(req, res, next) { // Expose the promise we will resolve after our pre-loading ghost.loaded = loading.promise; -when.all([ghost.init(), helpers.loadCoreHelpers(ghost)]).then(function () { +when(ghost.init()).then(function () { + return helpers.loadCoreHelpers(ghost); +}).then(function () { // ##Configuration var oneYear = 31536000000; // Logging configuration - if (server.get('env') !== "development") { + if (server.get('env') !== 'development') { server.use(express.logger()); } else { server.use(express.logger('dev')); @@ -243,7 +246,7 @@ when.all([ghost.init(), helpers.loadCoreHelpers(ghost)]).then(function () { server.use('/shared', express['static'](path.join(__dirname, '/shared'))); server.use('/content/images', express['static'](path.join(__dirname, '/../content/images'))); // Serve our built scripts; can't use /scripts here because themes already are - server.use("/built/scripts", express['static'](path.join(__dirname, '/built/scripts'), { + server.use('/built/scripts', express['static'](path.join(__dirname, '/built/scripts'), { // Put a maxAge of one year on built scripts maxAge: oneYear })); @@ -264,6 +267,7 @@ when.all([ghost.init(), helpers.loadCoreHelpers(ghost)]).then(function () { server.use(express.urlencoded()); 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 }})); @@ -339,7 +343,11 @@ when.all([ghost.init(), helpers.loadCoreHelpers(ghost)]).then(function () { 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.get(/^\/(ghost$|(ghost-admin|admin|wp-admin|dashboard|signin)\/?)/, auth, function (req, res) { + // 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/'); + }); + server.get(/^\/(ghost$\/?)/, auth, function (req, res) { res.redirect('/ghost/'); }); server.get('/ghost/', redirectToSignup, auth, admin.index); diff --git a/core/server/api.js b/core/server/api.js index 41e73da570..273fca757f 100644 --- a/core/server/api.js +++ b/core/server/api.js @@ -1,14 +1,14 @@ // # Ghost Data API // Provides access to the data model -var Ghost = require('../ghost'), - _ = require('underscore'), - when = require('when'), - errors = require('./errorHandling'), - permissions = require('./permissions'), - canThis = permissions.canThis, +var Ghost = require('../ghost'), + _ = require('underscore'), + when = require('when'), + errors = require('./errorHandling'), + permissions = require('./permissions'), + canThis = permissions.canThis, - ghost = new Ghost(), + ghost = new Ghost(), dataProvider = ghost.dataProvider, posts, users, @@ -228,7 +228,7 @@ settingsCollection = function (settings) { settingsFilter = function (settings, filter) { return _.object(_.filter(_.pairs(settings), function (setting) { if (filter) { - return _.some(filter.split(","), function (f) { + return _.some(filter.split(','), function (f) { return setting[1].type === f; }); } diff --git a/core/server/controllers/admin.js b/core/server/controllers/admin.js index 0ee8e461f8..36d97de7cd 100644 --- a/core/server/controllers/admin.js +++ b/core/server/controllers/admin.js @@ -1,17 +1,17 @@ -var Ghost = require('../../ghost'), - dataExport = require('../data/export'), - dataImport = require('../data/import'), - _ = require('underscore'), - fs = require('fs-extra'), - path = require('path'), - when = require('when'), - nodefn = require('when/node/function'), - api = require('../api'), - moment = require('moment'), - errors = require('../errorHandling'), +var Ghost = require('../../ghost'), + dataExport = require('../data/export'), + dataImport = require('../data/import'), + _ = require('underscore'), + fs = require('fs-extra'), + path = require('path'), + when = require('when'), + nodefn = require('when/node/function'), + api = require('../api'), + moment = require('moment'), + errors = require('../errorHandling'), - ghost = new Ghost(), - dataProvider = ghost.dataProvider, + ghost = new Ghost(), + dataProvider = ghost.dataProvider, adminNavbar, adminControllers, loginSecurity = []; @@ -269,8 +269,8 @@ adminControllers = { // TODO: Centralise list/enumeration of settings panes, so we don't // run into trouble in future. - var allowedSections = ["", "general", "user"], - section = req.url.replace(/(^\/ghost\/settings[\/]*|\/$)/ig, ""); + var allowedSections = ['', 'general', 'user'], + section = req.url.replace(/(^\/ghost\/settings[\/]*|\/$)/ig, ''); if (allowedSections.indexOf(section) < 0) { return next(); @@ -312,7 +312,7 @@ adminControllers = { }; return api.notifications.add(notification).then(function () { - res.redirect("/ghost/debug/"); + res.redirect('/ghost/debug/'); }); }); }, @@ -327,16 +327,16 @@ adminControllers = { }; return api.notifications.add(notification).then(function () { - res.redirect("/ghost/debug/"); + res.redirect('/ghost/debug/'); }); } // Get the current version for importing - api.settings.read({ key: "databaseVersion" }) + api.settings.read({ key: 'databaseVersion' }) .then(function (setting) { return when(setting.value); }, function () { - return when("001"); + return when('001'); }) .then(function (databaseVersion) { // Read the file contents diff --git a/core/server/controllers/frontend.js b/core/server/controllers/frontend.js index e9217717ef..38bf0e9edb 100644 --- a/core/server/controllers/frontend.js +++ b/core/server/controllers/frontend.js @@ -4,14 +4,16 @@ /*global require, module */ -var Ghost = require('../../ghost'), - api = require('../api'), - RSS = require('rss'), - _ = require('underscore'), +var Ghost = require('../../ghost'), + api = require('../api'), + RSS = require('rss'), + _ = require('underscore'), errors = require('../errorHandling'), - when = require('when'), + when = require('when'), + url = require('url'), - ghost = new Ghost(), + + ghost = new Ghost(), frontendControllers; frontendControllers = { @@ -24,7 +26,7 @@ frontendControllers = { // No negative pages if (isNaN(pageParam) || pageParam < 1) { //redirect to 404 page? - return res.redirect("/"); + return res.redirect('/'); } options.page = pageParam; @@ -87,14 +89,14 @@ frontendControllers = { description: ghost.settings('description'), generator: 'Ghost v' + res.locals.version, author: user ? user.attributes.name : null, - feed_url: siteUrl + '/rss/', + feed_url: url.resolve(siteUrl, '/rss/'), site_url: siteUrl, ttl: '60' }); // No negative pages if (isNaN(pageParam) || pageParam < 1) { - return res.redirect("/rss/"); + return res.redirect('/rss/'); } if (pageParam === 1 && req.route.path === '/rss/:page/') { @@ -112,22 +114,30 @@ frontendControllers = { // If page is greater than number of pages we have, redirect to last page if (pageParam > maxPage) { - return res.redirect("/rss/" + maxPage + "/"); + return res.redirect('/rss/' + maxPage + '/'); } ghost.doFilter('prePostsRender', page.posts, function (posts) { posts.forEach(function (post) { var item = { - title: _.escape(post.title), - guid: post.uuid, - url: siteUrl + '/' + post.slug + '/', - date: post.published_at - }; - - if (post.meta_description !== null) { - item.push({ description: post.meta_description }); - } + title: _.escape(post.title), + guid: post.uuid, + url: siteUrl + '/' + post.slug + '/', + date: post.published_at, + }, + content = post.html; + //set img src to absolute url + content = content.replace(/src=["|'|\s]?([\w\/\?\$\.\+\-;%:@&=,_]+)["|'|\s]?/gi, function (match, p1) { + p1 = url.resolve(siteUrl, p1); + return "src='" + p1 + "' "; + }); + //set a href to absolute url + content = content.replace(/href=["|'|\s]?([\w\/\?\$\.\+\-;%:@&=,_]+)["|'|\s]?/gi, function (match, p1) { + p1 = url.resolve(siteUrl, p1); + return "href='" + p1 + "' "; + }); + item.description = content; feed.item(item); }); res.set('Content-Type', 'text/xml'); @@ -138,6 +148,7 @@ frontendControllers = { return next(new Error(err)); }); } + }; module.exports = frontendControllers; \ No newline at end of file diff --git a/core/server/data/export/index.js b/core/server/data/export/index.js index ed33f11135..432e45763c 100644 --- a/core/server/data/export/index.js +++ b/core/server/data/export/index.js @@ -1,8 +1,8 @@ -var when = require('when'), - _ = require('underscore'), +var when = require('when'), + _ = require('underscore'), migration = require('../migration'), - client = require('../../models/base').client, - knex = require('../../models/base').Knex, + client = require('../../models/base').client, + knex = require('../../models/base').Knex, exporter; @@ -15,7 +15,7 @@ function getTablesFromSqlite3() { } function getTablesFromMySQL() { - knex.Raw("show tables").then(function (response) { + return knex.Raw('show tables').then(function (response) { return _.flatten(_.map(response, function (entry) { return _.values(entry); })); diff --git a/core/server/data/fixtures/index.js b/core/server/data/fixtures/index.js index a60ef16ebd..a0754c742a 100644 --- a/core/server/data/fixtures/index.js +++ b/core/server/data/fixtures/index.js @@ -1,10 +1,10 @@ -var sequence = require('when/sequence'), - _ = require('underscore'), - Post = require('../../models/post').Post, - Tag = require('../../models/tag').Tag, - Role = require('../../models/role').Role, +var sequence = require('when/sequence'), + _ = require('underscore'), + Post = require('../../models/post').Post, + Tag = require('../../models/tag').Tag, + Role = require('../../models/role').Role, Permission = require('../../models/permission').Permission, - uuid = require('node-uuid'); + uuid = require('node-uuid'); var fixtures = { posts: [ @@ -12,7 +12,7 @@ var fixtures = { "title": "Welcome to Ghost", "slug": "welcome-to-ghost", "markdown": "You're in! Nice. We've put together a little post to introduce you to the Ghost editor and get you started. Go ahead and edit this post to get going and learn how it all works!\n\n## Getting Started\n\nWriting in markdown is really easy. In the left hand panel of Ghost, you simply write as you normally would. Where appropriate, you can use *formatting* shortcuts to style your content. For example, a list:\n\n* Item number one\n* Item number two\n * A nested item\n* A final item\n\nor with numbers!\n\n1. Remember to buy some milk\n2. Drink the milk\n3. Tweet that I remembered to buy the milk, and drank it\n\n### Links\n\nWant to link to a source? No problem. If you paste in url, like http://ghost.org - it'll automatically be linked up. But if you want to customise your anchor text, you can do that too! Here's a link to [the Ghost website](http://ghost.org). Neat.\n\n### What about Images?\n\nImages work too! Already know the URL of the image you want to include in your article? Simply paste it in like this to make it show up:\n\n![The Ghost Logo](http://tryghost.org/ghost.png)\n\nNot sure which image you want to use yet? That's ok too. Leave yourself a descriptive placeholder and keep writing. Come back later and drag and drop the image in to upload:\n\n![A bowl of bananas]\n\n\n### Quoting\n\nSometimes a link isn't enough, you want to quote someone on what they've said. It was probably very wisdomous. Is wisdomous a word? Find out in a future release when we introduce spellcheck! For now - it's definitely a word.\n\n> Wisdomous - it's definitely a word.\n\n### Working with Code\n\nGot a streak of geek? We've got you covered there, too. You can write inline `` blocks really easily with back ticks. Want to show off something more comprehensive? 4 spaces of indentation gets you there.\n\n .awesome-thing {\n display: block;\n width: 100%;\n }\n\n### Ready for a Break? \n\nThrow 3 or more dashes down on any new line and you've got yourself a fancy new divider. Aw yeah.\n\n---\n\n### Advanced Usage\n\nThere's one fantastic secret about Markdown. If you want, you can write plain old HTML and it'll still work! Very flexible.\n\n\n\nThat should be enough to get you started. Have fun - and let us know what you think :)", - "html": "

You're in! Nice. We've put together a little post to introduce you to the Ghost editor and get you started. Go ahead and edit this post to get going and learn how it all works!

\n\n

Getting Started

\n\n

Writing in markdown is really easy. In the left hand panel of Ghost, you simply write as you normally would. Where appropriate, you can use formatting shortcuts to style your content. For example, a list:

\n\n\n\n

or with numbers!

\n\n
    \n
  1. Remember to buy some milk
  2. \n
  3. Drink the milk
  4. \n
  5. Tweet that I remembered to buy the milk, and drank it
  6. \n
\n\n

Links

\n\n

Want to link to a source? No problem. If you paste in url, like http://ghost.org - it'll automatically be linked up. But if you want to customise your anchor text, you can do that too! Here's a link to the Ghost website. Neat.

\n\n

What about Images?

\n\n

Images work too! Already know the URL of the image you want to include in your article? Simply paste it in like this to make it show up:

\n\n

\"The

\n\n

Not sure which image you want to use yet? That's ok too. Leave yourself a descriptive placeholder and keep writing. Come back later and drag and drop the image in to upload:

\n\n

\"A

\n\n

Quoting

\n\n

Sometimes a link isn't enough, you want to quote someone on what they've said. It was probably very wisdomous. Is wisdomous a word? Find out in a future release when we introduce spellcheck! For now - it's definitely a word.

\n\n
\n

Wisdomous - it's definitely a word.

\n
\n\n

Working with Code

\n\n

Got a streak of geek? We've got you covered there, too. You can write inline <code> blocks really easily with back ticks. Want to show off something more comprehensive? 4 spaces of indentation gets you there.

\n\n
.awesome-thing {\n    display: block;\n    width: 100%;\n}\n
\n\n

Ready for a Break?

\n\n

Throw 3 or more dashes down on any new line and you've got yourself a fancy new divider. Aw yeah.

\n\n
\n\n

Advanced Usage

\n\n

There's one fantastic secret about Markdown. If you want, you can write plain old HTML and it'll still work! Very flexible.

\n\n

\n\n

That should be enough to get you started. Have fun - and let us know what you think :)

", + "html": "

You're in! Nice. We've put together a little post to introduce you to the Ghost editor and get you started. Go ahead and edit this post to get going and learn how it all works!

\n\n

Getting Started

\n\n

Writing in markdown is really easy. In the left hand panel of Ghost, you simply write as you normally would. Where appropriate, you can use formatting shortcuts to style your content. For example, a list:

\n\n\n\n

or with numbers!

\n\n
    \n
  1. Remember to buy some milk
  2. \n
  3. Drink the milk
  4. \n
  5. Tweet that I remembered to buy the milk, and drank it
  6. \n
\n\n

Links

\n\n

Want to link to a source? No problem. If you paste in url, like http://ghost.org - it'll automatically be linked up. But if you want to customise your anchor text, you can do that too! Here's a link to the Ghost website. Neat.

\n\n

What about Images?

\n\n

Images work too! Already know the URL of the image you want to include in your article? Simply paste it in like this to make it show up:

\n\n

\"The

\n\n

Not sure which image you want to use yet? That's ok too. Leave yourself a descriptive placeholder and keep writing. Come back later and drag and drop the image in to upload:

\n\n

Quoting

\n\n

Sometimes a link isn't enough, you want to quote someone on what they've said. It was probably very wisdomous. Is wisdomous a word? Find out in a future release when we introduce spellcheck! For now - it's definitely a word.

\n\n
\n

Wisdomous - it's definitely a word.

\n
\n\n

Working with Code

\n\n

Got a streak of geek? We've got you covered there, too. You can write inline <code> blocks really easily with back ticks. Want to show off something more comprehensive? 4 spaces of indentation gets you there.

\n\n
.awesome-thing {\n    display: block;\n    width: 100%;\n}\n
\n\n

Ready for a Break?

\n\n

Throw 3 or more dashes down on any new line and you've got yourself a fancy new divider. Aw yeah.

\n\n
\n\n

Advanced Usage

\n\n

There's one fantastic secret about Markdown. If you want, you can write plain old HTML and it'll still work! Very flexible.

\n\n

\n\n

That should be enough to get you started. Have fun - and let us know what you think :)

", "image": null, "featured": false, "page": false, diff --git a/core/server/data/import/000.js b/core/server/data/import/000.js index 9c2bdd8fa8..73cdf49b7d 100644 --- a/core/server/data/import/000.js +++ b/core/server/data/import/000.js @@ -1,19 +1,19 @@ -var when = require("when"), - _ = require("underscore"), +var when = require('when'), + _ = require('underscore'), models = require('../../models'), errors = require('../../errorHandling'), Importer000; Importer000 = function () { - _.bindAll(this, "basicImport"); + _.bindAll(this, 'basicImport'); - this.version = "000"; + this.version = '000'; this.importFrom = { - "000": this.basicImport, - "001": this.tempImport, - "002": this.tempImport + '000': this.basicImport, + '001': this.tempImport, + '002': this.tempImport }; }; @@ -81,7 +81,12 @@ function preProcessPostTags(tableData) { function importTags(ops, tableData) { tableData = stripProperties(['id'], tableData); _.each(tableData, function (tag) { - ops.push(models.Tag.add(tag)); + ops.push(models.Tag.read({name: tag.name}).then(function (_tag) { + if (!_tag) { + return models.Tag.add(tag); + } + return when.resolve(_tag); + })); }); } diff --git a/core/server/data/import/index.js b/core/server/data/import/index.js index cab3327250..6bfb98e216 100644 --- a/core/server/data/import/index.js +++ b/core/server/data/import/index.js @@ -4,7 +4,7 @@ module.exports = function (version, data) { var importer; try { - importer = require("./" + version); + importer = require('./' + version); } catch (ignore) { // Zero effs given } diff --git a/core/server/data/migration/000.js b/core/server/data/migration/000.js index d5b4bf1aab..003e62b3c5 100644 --- a/core/server/data/migration/000.js +++ b/core/server/data/migration/000.js @@ -131,22 +131,22 @@ up = 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"), - knex.Schema.dropTableIfExists("tags") + 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 () { - // 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("posts_tags") + knex.Schema.dropTableIfExists('roles'), + knex.Schema.dropTableIfExists('settings'), + knex.Schema.dropTableIfExists('permissions'), + knex.Schema.dropTableIfExists('tags'), + knex.Schema.dropTableIfExists('posts') ]); }); }; -exports.up = up; +exports.up = up; exports.down = down; \ No newline at end of file diff --git a/core/server/data/migration/index.js b/core/server/data/migration/index.js index 8b62da0dd1..00e2523773 100644 --- a/core/server/data/migration/index.js +++ b/core/server/data/migration/index.js @@ -1,15 +1,15 @@ -var _ = require('underscore'), - when = require('when'), - series = require('when/sequence'), - errors = require('../../errorHandling'), - knex = require('../../models/base').Knex, +var _ = require('underscore'), + when = require('when'), + series = require('when/sequence'), + errors = require('../../errorHandling'), + knex = require('../../models/base').Knex, defaultSettings = require('../default-settings'), - Settings = require('../../models/settings').Settings, - fixtures = require('../fixtures'), + Settings = require('../../models/settings').Settings, + fixtures = require('../fixtures'), - initialVersion = '000', + initialVersion = '000', defaultDatabaseVersion; // Default Database Version diff --git a/core/server/errorHandling.js b/core/server/errorHandling.js index 8da0c69373..4cc201b2aa 100644 --- a/core/server/errorHandling.js +++ b/core/server/errorHandling.js @@ -1,16 +1,16 @@ /*jslint regexp: true */ -var _ = require('underscore'), - colors = require("colors"), - fs = require('fs'), - path = require('path'), +var _ = require('underscore'), + colors = require('colors'), + fs = require('fs'), + path = require('path'), errors, // Paths for views - appRoot = path.resolve(__dirname, '../'), - themePath = path.resolve(appRoot + '/content/themes'), - adminTemplatePath = path.resolve(appRoot + '/server/views/'), + appRoot = path.resolve(__dirname, '../'), + themePath = path.resolve(appRoot + '/content/themes'), + adminTemplatePath = path.resolve(appRoot + '/server/views/'), defaultErrorTemplatePath = path.resolve(adminTemplatePath + '/user-error.hbs'), - userErrorTemplatePath = path.resolve(themePath + '/error.hbs'), + userErrorTemplatePath = path.resolve(themePath + '/error.hbs'), userErrorTemplateExists; /** @@ -31,14 +31,14 @@ errors = { logError: function (err, context, help) { var stack = err ? err.stack : null; - err = err.message || err || "Unknown"; + err = err.message || err || 'Unknown'; // TODO: Logging framework hookup // Eventually we'll have better logging which will know about envs if ((process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'staging' || process.env.NODE_ENV === 'production')) { - console.error("\nERROR:".red, err.red); + console.error('\nERROR:'.red, err.red); if (context) { console.error(context); @@ -49,10 +49,10 @@ errors = { } // add a new line - console.error(""); + console.error(''); if (stack) { - console.error(stack, "\n"); + console.error(stack, '\n'); } } }, @@ -84,7 +84,7 @@ errors = { renderErrorPage: function (code, err, req, res, next) { function parseStack(stack) { - if (typeof stack !== "string") { + if (typeof stack !== 'string') { return stack; } @@ -102,8 +102,8 @@ errors = { } return { - "function": parts[1], - "at": parts[2] + 'function': parts[1], + 'at': parts[2] }; }) .filter(function (line) { @@ -116,12 +116,12 @@ errors = { function renderErrorInt(errorView) { var stack = null; - if (process.env.NODE_ENV !== "production" && err.stack) { + if (process.env.NODE_ENV !== 'production' && err.stack) { stack = parseStack(err.stack); } // TODO: Attach node-polyglot - res.render((errorView || "error"), { + res.render((errorView || 'error'), { message: err.message || err, code: code, stack: stack @@ -177,13 +177,13 @@ errors = { // Ensure our 'this' context in the functions _.bindAll( errors, - "throwError", - "logError", - "logAndThrowError", - "logErrorWithRedirect", - "renderErrorPage", - "render404Page", - "render500Page" + 'throwError', + 'logError', + 'logAndThrowError', + 'logErrorWithRedirect', + 'renderErrorPage', + 'render404Page', + 'render500Page' ); module.exports = errors; \ No newline at end of file diff --git a/core/server/helpers/index.js b/core/server/helpers/index.js index df3b9144e5..85796b64e5 100644 --- a/core/server/helpers/index.js +++ b/core/server/helpers/index.js @@ -1,18 +1,18 @@ -var _ = require('underscore'), - moment = require('moment'), - downsize = require('downsize'), - when = require('when'), - hbs = require('express-hbs'), +var _ = require('underscore'), + moment = require('moment'), + downsize = require('downsize'), + when = require('when'), + hbs = require('express-hbs'), packageInfo = require('../../../package.json'), - errors = require('../errorHandling'), - models = require('../models'), + errors = require('../errorHandling'), + models = require('../models'), coreHelpers; coreHelpers = function (ghost) { var paginationHelper, scriptTemplate = _.template(""), - isProduction = process.env.NODE_ENV === "production", + isProduction = process.env.NODE_ENV === 'production', version = encodeURIComponent(packageInfo.version); /** @@ -34,7 +34,7 @@ coreHelpers = function (ghost) { } } - var f = options.hash.format || "MMM Do, YYYY", + var f = options.hash.format || 'MMM Do, YYYY', timeago = options.hash.timeago, date; @@ -76,7 +76,7 @@ coreHelpers = function (ghost) { } if (models.isPost(this)) { - output += "/" + this.slug + '/'; + output += '/' + this.slug + '/'; } return output; @@ -91,7 +91,7 @@ coreHelpers = function (ghost) { // if the author could not be determined. // ghost.registerThemeHelper('author', function (context, options) { - return this.author ? this.author.name : ""; + return this.author ? this.author.name : ''; }); // ### Tags Helper @@ -106,10 +106,10 @@ coreHelpers = function (ghost) { // Note that the standard {{#each tags}} implementation is unaffected by this helper // and can be used for more complex templates. ghost.registerThemeHelper('tags', function (options) { - var separator = ", ", + var separator = ', ', tagNames; - if (typeof options.hash.separator === "string") { + if (typeof options.hash.separator === 'string') { separator = options.hash.separator; } @@ -133,7 +133,7 @@ coreHelpers = function (ghost) { // ghost.registerThemeHelper('content', function (options) { var truncateOptions = (options || {}).hash || {}; - truncateOptions = _.pick(truncateOptions, ["words", "characters"]); + truncateOptions = _.pick(truncateOptions, ['words', 'characters']); if (truncateOptions.words || truncateOptions.characters) { return new hbs.handlebars.SafeString( @@ -162,10 +162,10 @@ coreHelpers = function (ghost) { var truncateOptions = (options || {}).hash || {}, excerpt; - truncateOptions = _.pick(truncateOptions, ["words", "characters"]); + truncateOptions = _.pick(truncateOptions, ['words', 'characters']); /*jslint regexp:true */ - excerpt = String(this.html).replace(/<\/?[^>]+>/gi, ""); + excerpt = String(this.html).replace(/<\/?[^>]+>/gi, ''); /*jslint regexp:false */ if (!truncateOptions.words && !truncateOptions.characters) { @@ -198,7 +198,7 @@ coreHelpers = function (ghost) { var classes = ['post']; if (this.tags) { - classes = classes.concat(this.tags.map(function (tag) { return "tag-" + tag.name; })); + classes = classes.concat(this.tags.map(function (tag) { return 'tag-' + tag.name; })); } return ghost.doFilter('post_class', classes, function (classes) { @@ -208,12 +208,15 @@ coreHelpers = function (ghost) { }); ghost.registerThemeHelper('ghost_head', function (options) { - var head = []; - head.push(''); + var head = [], + majorMinor = /^(\d+\.)?(\d+)/, + trimmedVersion = this.version.match(majorMinor)[0]; + + head.push(''); head.push(''); return ghost.doFilter('ghost_head', head, function (head) { - var headString = _.reduce(head, function (memo, item) { return memo + "\n" + item; }, ''); + var headString = _.reduce(head, function (memo, item) { return memo + '\n' + item; }, ''); return new hbs.handlebars.SafeString(headString.trim()); }); }); @@ -292,7 +295,7 @@ coreHelpers = function (ghost) { j = 0, columns = options.hash.columns, key, - ret = "", + ret = '', data; if (options.data) { @@ -356,18 +359,18 @@ coreHelpers = function (ghost) { }); // A helper for inserting the javascript tags with version hashes - ghost.registerThemeHelper("ghostScriptTags", function () { + ghost.registerThemeHelper('ghostScriptTags', function () { var scriptFiles = []; if (isProduction) { scriptFiles.push("ghost.min.js"); } else { scriptFiles = [ - "vendor.js", - "helpers.js", - "templates.js", - "models.js", - "views.js" + 'vendor.js', + 'helpers.js', + 'templates.js', + 'models.js', + 'views.js' ]; } @@ -378,7 +381,7 @@ coreHelpers = function (ghost) { }); }); - return scriptFiles.join(""); + return scriptFiles.join(''); }); // ## Template driven helpers diff --git a/core/server/mail.js b/core/server/mail.js index 541e173d8c..990bc0608d 100644 --- a/core/server/mail.js +++ b/core/server/mail.js @@ -1,8 +1,8 @@ -var cp = require('child_process'), - url = require('url'), - _ = require('underscore'), - when = require('when'), - nodefn = require('when/node/function'), +var cp = require('child_process'), + url = require('url'), + _ = require('underscore'), + when = require('when'), + nodefn = require('when/node/function'), nodemailer = require('nodemailer'); function GhostMailer(opts) { @@ -51,7 +51,7 @@ GhostMailer.prototype.detectSendmail = function () { if (err && !/bin\/sendmail/.test(stdout)) { return reject(); } - resolve(stdout.toString()); + resolve(stdout.toString().replace(/(\n|\r|\r\n)$/, '')); }); }); }; diff --git a/core/server/models/base.js b/core/server/models/base.js index 24e9e37630..a8e2cd1638 100644 --- a/core/server/models/base.js +++ b/core/server/models/base.js @@ -1,10 +1,10 @@ var GhostBookshelf, Bookshelf = require('bookshelf'), - when = require('when'), - moment = require('moment'), - _ = require('underscore'), - uuid = require('node-uuid'), - config = require('../../../config'), + when = require('when'), + moment = require('moment'), + _ = require('underscore'), + uuid = require('node-uuid'), + config = require('../../../config'), Validator = require('validator').Validator; // Initializes Bookshelf as its own instance, so we can modify the Models and not mess up @@ -70,7 +70,7 @@ GhostBookshelf.Model = GhostBookshelf.Model.extend({ } _.each(relations, function (relation, key) { - if (key.substring(0, 7) !== "_pivot_") { + if (key.substring(0, 7) !== '_pivot_') { attrs[key] = relation.toJSON ? relation.toJSON() : relation; } }); @@ -126,7 +126,7 @@ GhostBookshelf.Model = GhostBookshelf.Model.extend({ //if slug is empty after trimming use "post" if (!slug) { - slug = "post"; + slug = 'post'; } // Test for duplicate slugs. return checkIfSlugExists(slug); diff --git a/core/server/models/index.js b/core/server/models/index.js index 991fe3741f..c91d737bf4 100644 --- a/core/server/models/index.js +++ b/core/server/models/index.js @@ -16,7 +16,7 @@ module.exports = { }); }, isPost: function (jsonData) { - return jsonData.hasOwnProperty("html") && jsonData.hasOwnProperty("markdown") - && jsonData.hasOwnProperty("title") && jsonData.hasOwnProperty("slug"); + return jsonData.hasOwnProperty('html') && jsonData.hasOwnProperty('markdown') + && jsonData.hasOwnProperty('title') && jsonData.hasOwnProperty('slug'); } }; diff --git a/core/server/models/permission.js b/core/server/models/permission.js index 8fa0688356..ccd3ffeaf4 100644 --- a/core/server/models/permission.js +++ b/core/server/models/permission.js @@ -1,6 +1,6 @@ var GhostBookshelf = require('./base'), - User = require('./user').User, - Role = require('./role').Role, + User = require('./user').User, + Role = require('./role').Role, Permission, Permissions; diff --git a/core/server/models/post.js b/core/server/models/post.js index 8e6d8f6fd1..74a197c263 100644 --- a/core/server/models/post.js +++ b/core/server/models/post.js @@ -128,7 +128,7 @@ Post = GhostBookshelf.Model.extend({ } }); - if (tagsToAttach) { + if (!_.isEmpty(tagsToAttach)) { return Tags.forge().query('whereIn', 'name', _.pluck(tagsToAttach, 'name')).fetch().then(function (matchingTags) { _.each(matchingTags.toJSON(), function (matchingTag) { tagOperations.push(self.tags().attach(matchingTag.id)); @@ -173,7 +173,7 @@ Post = GhostBookshelf.Model.extend({ // Extends base model findAll to eager-fetch author and user relationships. findAll: function (options) { options = options || {}; - options.withRelated = [ "author", "user", "tags" ]; + options.withRelated = [ 'author', 'user', 'tags' ]; return GhostBookshelf.Model.findAll.call(this, options); }, @@ -181,7 +181,7 @@ Post = GhostBookshelf.Model.extend({ // Extends base model findOne to eager-fetch author and user relationships. findOne: function (args, options) { options = options || {}; - options.withRelated = [ "author", "user", "tags" ]; + options.withRelated = [ 'author', 'user', 'tags' ]; return GhostBookshelf.Model.findOne.call(this, args, options); }, @@ -235,7 +235,7 @@ Post = GhostBookshelf.Model.extend({ postCollection.query('where', opts.where); } - opts.withRelated = [ "author", "user", "tags" ]; + opts.withRelated = [ 'author', 'user', 'tags' ]; // Set the limit & offset for the query, fetching // with the opts (to specify any eager relations, etc.) @@ -332,6 +332,19 @@ Post = GhostBookshelf.Model.extend({ // associated models can't be created until the post has an ID, so run this after return post.updateTags(newPostData.tags); }); + }, + destroy: function (_identifier, options) { + options = options || {}; + return this.forge({id: _identifier}).fetch({withRelated: ['tags']}).then(function destroyTags(post) { + var tagIds = _.pluck(post.related('tags').toJSON(), 'id'); + if (tagIds) { + return post.tags().detach(tagIds).then(function destroyPost() { + return post.destroy(options); + }); + } + + return post.destroy(options); + }); } }); diff --git a/core/server/models/role.js b/core/server/models/role.js index 2e960dc73f..6d0bd6962c 100644 --- a/core/server/models/role.js +++ b/core/server/models/role.js @@ -1,5 +1,5 @@ -var User = require('./user').User, - Permission = require('./permission').Permission, +var User = require('./user').User, + Permission = require('./permission').Permission, GhostBookshelf = require('./base'), Role, Roles; diff --git a/core/server/models/settings.js b/core/server/models/settings.js index ba6206a46d..5faf9e301a 100644 --- a/core/server/models/settings.js +++ b/core/server/models/settings.js @@ -1,10 +1,10 @@ var Settings, GhostBookshelf = require('./base'), - validator = GhostBookshelf.validator, - uuid = require('node-uuid'), - _ = require('underscore'), - errors = require('../errorHandling'), - when = require('when'), + validator = GhostBookshelf.validator, + uuid = require('node-uuid'), + _ = require('underscore'), + errors = require('../errorHandling'), + when = require('when'), defaultSettings; // For neatness, the defaults file is split into categories. diff --git a/core/server/models/tag.js b/core/server/models/tag.js index 4f20e3af5f..a1d972c80b 100644 --- a/core/server/models/tag.js +++ b/core/server/models/tag.js @@ -1,6 +1,6 @@ var Tag, Tags, - Posts = require('./post').Posts, + Posts = require('./post').Posts, GhostBookshelf = require('./base'); Tag = GhostBookshelf.Model.extend({ diff --git a/core/server/models/user.js b/core/server/models/user.js index f2898459de..894a3df261 100644 --- a/core/server/models/user.js +++ b/core/server/models/user.js @@ -1,20 +1,20 @@ var User, Users, - _ = 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, + _ = 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; + Role = require('./role').Role, + Permission = require('./permission').Permission; function validatePasswordLength(password) { try { - GhostBookshelf.validator.check(password, "Your password is not long enough. It 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); } @@ -33,10 +33,10 @@ User = GhostBookshelf.Model.extend({ ], validate: function () { - GhostBookshelf.validator.check(this.get('email'), "Please check your email address. It does not seem to be valid.").isEmail(); - GhostBookshelf.validator.check(this.get('bio'), "Your bio is too long. Please keep it to 200 chars.").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'), "Your website is not a valid URL.").isUrl(); + GhostBookshelf.validator.check(this.get('website'), "Looks like your website is not actually a website. Try again?").isUrl(); } return true; }, diff --git a/core/server/permissions/index.js b/core/server/permissions/index.js index fbd10a39ff..4059075acc 100644 --- a/core/server/permissions/index.js +++ b/core/server/permissions/index.js @@ -1,11 +1,11 @@ // 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, +var _ = require('underscore'), + when = require('when'), + Models = require('../models'), + objectTypeModelMap = require('./objectTypeModelMap'), + UserProvider = Models.User, PermissionsProvider = Models.Permission, init, refresh, diff --git a/core/server/permissions/objectTypeModelMap.js b/core/server/permissions/objectTypeModelMap.js index 74dd55e232..58efe95a69 100644 --- a/core/server/permissions/objectTypeModelMap.js +++ b/core/server/permissions/objectTypeModelMap.js @@ -1,7 +1,7 @@ module.exports = { - 'post': require('../models/post').Post, - 'role': require('../models/role').Role, - 'user': require('../models/user').User, + '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 + 'setting': require('../models/settings').Settings }; diff --git a/core/server/plugins/index.js b/core/server/plugins/index.js index c99e40c74d..468d0b9184 100644 --- a/core/server/plugins/index.js +++ b/core/server/plugins/index.js @@ -1,17 +1,17 @@ -var _ = require("underscore"), - when = require('when'), +var _ = require('underscore'), + when = require('when'), ghostApi, - loader = require("./loader"), - GhostPlugin = require("./GhostPlugin"); + loader = require('./loader'), + GhostPlugin = require('./GhostPlugin'); function getInstalledPlugins() { if (!ghostApi) { ghostApi = require('../api'); } - return ghostApi.settings.read("installedPlugins").then(function (installed) { - installed.value = installed.value || "[]"; + return ghostApi.settings.read('installedPlugins').then(function (installed) { + installed.value = installed.value || '[]'; try { installed = JSON.parse(installed.value); @@ -27,7 +27,7 @@ function saveInstalledPlugins(installedPlugins) { return getInstalledPlugins().then(function (currentInstalledPlugins) { var updatedPluginsInstalled = _.uniq(installedPlugins.concat(currentInstalledPlugins)); - return ghostApi.settings.edit("installedPlugins", updatedPluginsInstalled); + return ghostApi.settings.edit('installedPlugins', updatedPluginsInstalled); }); } diff --git a/core/server/plugins/loader.js b/core/server/plugins/loader.js index a26797a2da..26e3f0035b 100644 --- a/core/server/plugins/loader.js +++ b/core/server/plugins/loader.js @@ -1,7 +1,7 @@ -var path = require("path"), - _ = require("underscore"), - when = require("when"), +var path = require('path'), + _ = require('underscore'), + when = require('when'), ghostInstance, loader; @@ -10,7 +10,7 @@ function getGhostInstance() { return ghostInstance; } - var Ghost = require("../../ghost"); + var Ghost = require('../../ghost'); ghostInstance = new Ghost(); diff --git a/core/server/require-tree.js b/core/server/require-tree.js index dc4238b89b..902e860511 100644 --- a/core/server/require-tree.js +++ b/core/server/require-tree.js @@ -1,7 +1,7 @@ -var when = require('when'), - keys = require('when/keys'), - fs = require('fs'), - path = require('path'), +var when = require('when'), + keys = require('when/keys'), + fs = require('fs'), + path = require('path'), extend = function (obj, source) { var key; for (key in source) { diff --git a/core/server/views/debug.hbs b/core/server/views/debug.hbs index b309fbb623..841e828aeb 100644 --- a/core/server/views/debug.hbs +++ b/core/server/views/debug.hbs @@ -34,15 +34,6 @@ -
-
-
- - Reset -

Delete the entire database so you can start again with empty tables

-
-
-
\ No newline at end of file diff --git a/core/server/views/partials/navbar.hbs b/core/server/views/partials/navbar.hbs index 76d66dab93..ab4478f1b9 100644 --- a/core/server/views/partials/navbar.hbs +++ b/core/server/views/partials/navbar.hbs @@ -9,13 +9,13 @@