diff --git a/core/client/views/debug.js b/core/client/views/debug.js index 6d56d9e23d..42e3d005be 100644 --- a/core/client/views/debug.js +++ b/core/client/views/debug.js @@ -8,6 +8,59 @@ "click .js-delete": "handleDeleteClick" }, + initialize: function () { + // Disable import button and initizalize BlueImp file upload + $('#startupload').prop('disabled', true); + $('#importfile').fileupload({ + url: Ghost.paths.apiRoot + '/db/', + limitMultiFileUploads: 1, + replaceFileInput: false, + headers: { + 'X-CSRF-Token': $("meta[name='csrf-param']").attr('content') + }, + dataType: 'json', + add: function (e, data) { + /*jslint unparam:true*/ + data.context = $('#startupload').prop('disabled', false) + .click(function () { + $('#startupload').prop('disabled', true); + data.context = $('#startupload').text('Importing'); + data.submit(); + // unregister click event to allow different subsequent uploads + $('#startupload').off('click'); + }); + }, + done: function (e, data) { + /*jslint unparam:true*/ + $('#startupload').text('Import'); + if (!data.result) { + throw new Error('No response received from server.'); + } + if (!data.result.message) { + throw new Error('Unknown error'); + } + + Ghost.notifications.addItem({ + type: 'success', + message: data.result.message, + status: 'passive' + }); + }, + error: function (response) { + $('#startupload').text('Import'); + var responseJSON = response.responseJSON, + message = responseJSON && responseJSON.error ? responseJSON.error : 'unknown'; + Ghost.notifications.addItem({ + type: 'error', + message: ['A problem was encountered while importing new content to your blog. Error: ', message].join(''), + status: 'passive' + }); + } + + }); + + }, + handleMenuClick: function (ev) { ev.preventDefault(); @@ -21,6 +74,7 @@ return false; }, + handleDeleteClick: function (ev) { ev.preventDefault(); this.addSubview(new Ghost.Views.Modal({ diff --git a/core/server/api/db.js b/core/server/api/db.js index b683bfc39e..4c0e24d4e8 100644 --- a/core/server/api/db.js +++ b/core/server/api/db.js @@ -16,7 +16,7 @@ api.notifications = require('./notifications'); api.settings = require('./settings'); db = { - 'export': function (req, res) { + 'exportContent': function (req, res) { /*jslint unparam:true*/ return dataExport().then(function (exportedData) { // Save the exported data to the file system for download @@ -44,11 +44,10 @@ db = { }); }); }, - 'import': function (req, res) { - var notification, - databaseVersion; + 'importContent': function (options) { + var databaseVersion; - if (!req.files.importfile || !req.files.importfile.path || req.files.importfile.name.indexOf('json') === -1) { + if (!options.importfile || !options.importfile.path || options.importfile.name.indexOf('json') === -1) { /** * Notify of an error if it occurs * @@ -57,30 +56,17 @@ db = { * - If there is no path * - If the name doesn't have json in it */ - return api.notifications.browse().then(function (notifications) { - notification = { - type: 'error', - message: "Must select a .json file to import", - status: 'persistent', - id: 'per-' + (notifications.length + 1) - }; - - - - return api.notifications.add(notification).then(function () { - res.redirect(configPaths().debugPath); - }); - }); + return when.reject({errorCode: 500, message: 'Please select a .json file to import.'}); } - api.settings.read({ key: 'databaseVersion' }).then(function (setting) { + return api.settings.read({ key: 'databaseVersion' }).then(function (setting) { return when(setting.value); }, function () { return when('001'); }).then(function (version) { databaseVersion = version; // Read the file contents - return nodefn.call(fs.readFile, req.files.importfile.path); + return nodefn.call(fs.readFile, options.importfile.path); }).then(function (fileContents) { var importData, error = '', @@ -132,32 +118,9 @@ db = { }).then(function importSuccess() { return api.settings.updateSettingsCache(); }).then(function () { - return api.notifications.browse(); - }).then(function (notifications) { - notification = { - type: 'success', - message: "Posts, tags and other data successfully imported", - status: 'persistent', - id: 'per-' + (notifications.length + 1) - }; - - return api.notifications.add(notification).then(function () { - res.redirect(configPaths().debugPath); - }); + return when.resolve({message: 'Posts, tags and other data successfully imported'}); }).otherwise(function importFailure(error) { - return api.notifications.browse().then(function (notifications) { - // Notify of an error if it occurs - notification = { - type: 'error', - message: error.message || error, - status: 'persistent', - id: 'per-' + (notifications.length + 1) - }; - - return api.notifications.add(notification).then(function () { - res.redirect(configPaths().debugPath); - }); - }); + return when.reject({errorCode: 500, message: error.message || error}); }); }, 'deleteAllContent': function () { diff --git a/core/server/api/index.js b/core/server/api/index.js index 3a1e669d5b..e82e3ff46e 100644 --- a/core/server/api/index.js +++ b/core/server/api/index.js @@ -45,7 +45,7 @@ function cacheInvalidationHeader(req, result) { // takes the API method and wraps it so that it gets data from the request and returns a sensible JSON response requestHandler = function (apiMethod) { return function (req, res) { - var options = _.extend(req.body, req.query, req.params), + var options = _.extend(req.body, req.files, req.query, req.params), apiContext = { user: req.session && req.session.user }; diff --git a/core/server/middleware/index.js b/core/server/middleware/index.js index 506876ec55..236028ffb5 100644 --- a/core/server/middleware/index.js +++ b/core/server/middleware/index.js @@ -233,9 +233,6 @@ module.exports = function (server, dbHash) { expressServer.use(express.json()); expressServer.use(express.urlencoded()); - expressServer.use(subdir + '/ghost/upload/', middleware.busboy); - expressServer.use(subdir + '/ghost/api/v0.1/db/', middleware.busboy); - // ### Sessions cookie = { path: subdir + '/ghost', diff --git a/core/server/routes/admin.js b/core/server/routes/admin.js index d6c0146a0d..02f5413c07 100644 --- a/core/server/routes/admin.js +++ b/core/server/routes/admin.js @@ -43,8 +43,7 @@ module.exports = function (server) { server.get('/ghost/settings*', middleware.auth, admin.settings); server.get('/ghost/debug/', middleware.auth, admin.debug.index); - // We don't want to register bodyParser globally b/c of security concerns, so use multipart only here - server.post('/ghost/upload/', middleware.auth, admin.uploader); + server.post('/ghost/upload/', middleware.auth, middleware.busboy, admin.uploader); // redirect to /ghost and let that do the authentication to prevent redirects to /ghost//admin etc. server.get(/\/((ghost-admin|admin|wp-admin|dashboard|signin)\/?)$/, function (req, res) { diff --git a/core/server/routes/api.js b/core/server/routes/api.js index 39c2c6fa52..ffb0be2bc3 100644 --- a/core/server/routes/api.js +++ b/core/server/routes/api.js @@ -24,7 +24,7 @@ module.exports = function (server) { server.del('/ghost/api/v0.1/notifications/:id', middleware.authAPI, api.requestHandler(api.notifications.destroy)); server.post('/ghost/api/v0.1/notifications/', middleware.authAPI, api.requestHandler(api.notifications.add)); // #### Import/Export - server.get('/ghost/api/v0.1/db/', middleware.auth, api.db['export']); - server.post('/ghost/api/v0.1/db/', middleware.auth, api.db['import']); + server.get('/ghost/api/v0.1/db/', middleware.auth, api.db.exportContent); + server.post('/ghost/api/v0.1/db/', middleware.authAPI, middleware.busboy, api.requestHandler(api.db.importContent)); server.del('/ghost/api/v0.1/db/', middleware.authAPI, api.requestHandler(api.db.deleteAllContent)); }; \ No newline at end of file diff --git a/core/server/views/debug.hbs b/core/server/views/debug.hbs index 1fd7b9b824..0d993f3426 100644 --- a/core/server/views/debug.hbs +++ b/core/server/views/debug.hbs @@ -25,13 +25,12 @@ -