0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-10 23:36:14 -05:00

Merge pull request #3327 from ErisDS/issue-3284

Export 003
This commit is contained in:
Hannah Wolfe 2014-07-19 23:28:32 +01:00
commit 94fca08500
5 changed files with 112 additions and 54 deletions

View file

@ -19,9 +19,12 @@ var _ = require('lodash'),
slugs = require('./slugs'), slugs = require('./slugs'),
authentication = require('./authentication'), authentication = require('./authentication'),
uploads = require('./upload'), uploads = require('./upload'),
dataExport = require('../data/export'),
errors = require('../errors'),
http, http,
formatHttpErrors, formatHttpErrors,
addHeaders,
cacheInvalidationHeader, cacheInvalidationHeader,
locationHeader, locationHeader,
contentDispositionHeader, contentDispositionHeader,
@ -135,9 +138,9 @@ locationHeader = function (req, result) {
* @return {string} * @return {string}
*/ */
contentDispositionHeader = function () { contentDispositionHeader = function () {
// replace ':' with '_' for OS that don't support it return dataExport.fileName().then(function (filename) {
var now = (new Date()).toJSON().replace(/:/g, '_'); return 'Attachment; filename="' + filename + '"';
return 'Attachment; filename="ghost-' + now + '.json"'; });
}; };
@ -172,6 +175,51 @@ formatHttpErrors = function (error) {
return {errors: errors, statusCode: statusCode}; return {errors: errors, statusCode: statusCode};
}; };
addHeaders = function (apiMethod, req, res, result) {
var ops = [],
cacheInvalidation,
location,
contentDisposition;
cacheInvalidation = cacheInvalidationHeader(req, result)
.then(function addCacheHeader(header) {
if (header) {
res.set({'X-Cache-Invalidate': header});
}
});
ops.push(cacheInvalidation);
if (req.method === 'POST') {
location = locationHeader(req, result)
.then(function addLocationHeader(header) {
if (header) {
res.set({'Location': header});
// The location header indicates that a new object was created.
// In this case the status code should be 201 Created
res.status(201);
}
});
ops.push(location);
}
if (apiMethod === db.exportContent) {
contentDisposition = contentDispositionHeader()
.then(function addContentDispositionHeader(header) {
// Add Content-Disposition Header
if (apiMethod === db.exportContent) {
res.set({
'Content-Disposition': header
});
}
});
ops.push(contentDisposition);
}
return when.all(ops);
};
/** /**
* ### HTTP * ### HTTP
* *
@ -186,6 +234,7 @@ http = function (apiMethod) {
return function (req, res) { return function (req, res) {
// We define 2 properties for using as arguments in API calls: // We define 2 properties for using as arguments in API calls:
var object = req.body, var object = req.body,
response,
options = _.extend({}, req.files, req.query, req.params, { options = _.extend({}, req.files, req.query, req.params, {
context: { context: {
user: (req.user && req.user.id) ? req.user.id : null user: (req.user && req.user.id) ? req.user.id : null
@ -202,35 +251,16 @@ http = function (apiMethod) {
return apiMethod(object, options) return apiMethod(object, options)
// Handle adding headers // Handle adding headers
.then(function onSuccess(result) { .then(function onSuccess(result) {
response = result;
// Add X-Cache-Invalidate header // Add X-Cache-Invalidate header
return cacheInvalidationHeader(req, result) return addHeaders(apiMethod, req, res, result);
.then(function addCacheHeader(header) { }).then(function () {
if (header) { // #### Success
res.set({'X-Cache-Invalidate': header}); // Send a properly formatting HTTP response containing the data with correct headers
} console.log(response);
res.json(response || {});
// Add Location header
return locationHeader(req, result);
}).then(function addLocationHeader(header) {
if (header) {
res.set({'Location': header});
// The location header indicates that a new object was created.
// In this case the status code should be 201 Created
res.status(201);
}
// Add Content-Disposition Header
if (apiMethod === db.exportContent) {
res.set({
'Content-Disposition': contentDispositionHeader()
});
}
// #### Success
// Send a properly formatting HTTP response containing the data with correct headers
res.json(result || {});
});
}).catch(function onError(error) { }).catch(function onError(error) {
errors.logError(error);
// #### Error // #### Error
var httpErrors = formatHttpErrors(error); var httpErrors = formatHttpErrors(error);
// Send a properly formatted HTTP response containing the errors // Send a properly formatted HTTP response containing the errors

View file

@ -1,11 +1,31 @@
var _ = require('lodash'), var _ = require('lodash'),
when = require('when'), when = require('when'),
versioning = require('../versioning'),
config = require('../../config'),
utils = require('../utils'),
excludedTables = [], versioning = require('../versioning'),
exporter; config = require('../../config'),
utils = require('../utils'),
serverUtils = require('../../utils'),
errors = require('../../errors'),
settings = require('../../api/settings'),
excludedTables = ['accesstokens', 'refreshtokens', 'clients'],
exporter,
exportFileName;
exportFileName = function () {
var datetime = (new Date()).toJSON().substring(0, 10),
title = '';
return settings.read({key: 'title', context: {internal: true}}).then(function (result) {
if (result) {
title = serverUtils.safeString(result.settings[0].value) + '.';
}
return title + 'ghost.' + datetime + '.json';
}).catch(function (err) {
errors.logError(err);
return 'ghost.' + datetime + '.json';
});
};
exporter = function () { exporter = function () {
return when.join(versioning.getDatabaseVersion(), utils.getTables()).then(function (results) { return when.join(versioning.getDatabaseVersion(), utils.getTables()).then(function (results) {
@ -34,9 +54,10 @@ exporter = function () {
return when.resolve(exportData); return when.resolve(exportData);
}).catch(function (err) { }).catch(function (err) {
console.log('Error exporting data: ' + err); errors.logAndThrowError(err, 'Error exporting data', '');
}); });
}); });
}; };
module.exports = exporter; module.exports = exporter;
module.exports.fileName = exportFileName;

View file

@ -44,7 +44,7 @@ backupDatabase = function backupDatabase() {
logInfo('Creating database backup'); logInfo('Creating database backup');
return dataExport().then(function (exportedData) { return dataExport().then(function (exportedData) {
// Save the exported data to the file system for download // Save the exported data to the file system for download
var fileName = path.resolve(config().paths.contentPath + '/data/exported-' + (new Date().getTime()) + '.json'); var fileName = path.resolve(config().paths.contentPath + '/data/' + dataExport.fileName());
return nodefn.call(fs.writeFile, fileName, JSON.stringify(exportedData)).then(function () { return nodefn.call(fs.writeFile, fileName, JSON.stringify(exportedData)).then(function () {
logInfo('Database backup written to: ' + fileName); logInfo('Database backup written to: ' + fileName);

View file

@ -11,7 +11,7 @@ var bookshelf = require('bookshelf'),
_ = require('lodash'), _ = require('lodash'),
uuid = require('node-uuid'), uuid = require('node-uuid'),
config = require('../config'), config = require('../config'),
unidecode = require('unidecode'), utils = require('../utils'),
sanitize = require('validator').sanitize, sanitize = require('validator').sanitize,
schema = require('../data/schema'), schema = require('../data/schema'),
validation = require('../data/validation'), validation = require('../data/validation'),
@ -343,19 +343,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
}); });
}; };
slug = base.trim(); slug = utils.safeString(base);
// Remove non ascii characters
slug = unidecode(slug);
// Remove URL reserved chars: `:/?#[]@!$&'()*+,;=` as well as `\%<>|^~£"`
slug = slug.replace(/[:\/\?#\[\]@!$&'()*+,;=\\%<>\|\^~£"]/g, '')
// Replace dots and spaces with a dash
.replace(/(\s|\.)/g, '-')
// Convert 2 or more dashes into a single dash
.replace(/-+/g, '-')
// Make the whole thing lowercase
.toLowerCase();
// Remove trailing hyphen // Remove trailing hyphen
slug = slug.charAt(slug.length - 1) === '-' ? slug.substr(0, slug.length - 1) : slug; slug = slug.charAt(slug.length - 1) === '-' ? slug.substr(0, slug.length - 1) : slug;

View file

@ -1,4 +1,6 @@
var utils, var unidecode = require('unidecode'),
utils,
getRandomInt; getRandomInt;
/** /**
@ -35,6 +37,23 @@ utils = {
} }
return buf.join(''); return buf.join('');
},
safeString: function (string) {
string = string.trim();
// Remove non ascii characters
string = unidecode(string);
// Remove URL reserved chars: `:/?#[]@!$&'()*+,;=` as well as `\%<>|^~£"`
string = string.replace(/[:\/\?#\[\]@!$&'()*+,;=\\%<>\|\^~£"]/g, '')
// Replace dots and spaces with a dash
.replace(/(\s|\.)/g, '-')
// Convert 2 or more dashes into a single dash
.replace(/-+/g, '-')
// Make the whole thing lowercase
.toLowerCase();
return string;
} }
}; };