mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-26 20:34:02 +03:00
af6137248d
fixes #1765 fixes #1811 issue #1833 New UrlFor functions - moved body of url helper to config.path.urlFor, which can generate a URL for various scenarios - urlFor can take a string (name) or object (relativeUrl: '/') as the first argument - this is the first step towards issue #1833 - also added config.path.urlForPost which is async and handles getting permalink setting - frontend controller, ghost_head helper, cache invalidation all now use urlFor or urlForPost all urls should be correct and consistent URL Consistency Improvements - refactored invalidateCache into cacheInvalidationHeader which returns a promise so that url can be generated properly by urlForPost - moved isPost from models to schema, and refactored schema to have a tables object - deleted posts now return the whole object, not just id and slug, ensuring cache invalidation header can be set on delete - frontend controller rss and archive page redirects work properly with subdirectory - removes {{url}} helper from admin and client, and replaced with adminUrl helper which also uses urlFor - in res.locals ghostRoot becomes relativeUrl, and path is removed
173 lines
7.0 KiB
JavaScript
173 lines
7.0 KiB
JavaScript
var dataExport = require('../data/export'),
|
|
dataImport = require('../data/import'),
|
|
dataProvider = require('../models'),
|
|
fs = require('fs-extra'),
|
|
path = require('path'),
|
|
when = require('when'),
|
|
nodefn = require('when/node/function'),
|
|
_ = require('underscore'),
|
|
schema = require('../data/schema').tables,
|
|
configPaths = require('../config/paths'),
|
|
api = {},
|
|
|
|
db;
|
|
|
|
api.notifications = require('./notifications');
|
|
api.settings = require('./settings');
|
|
|
|
db = {
|
|
'export': function (req, res) {
|
|
/*jslint unparam:true*/
|
|
return dataExport().then(function (exportedData) {
|
|
// Save the exported data to the file system for download
|
|
var fileName = path.join(configPaths().exportPath, 'exported-' + (new Date().getTime()) + '.json');
|
|
|
|
return nodefn.call(fs.writeFile, fileName, JSON.stringify(exportedData)).then(function () {
|
|
return when(fileName);
|
|
});
|
|
}).then(function (exportedFilePath) {
|
|
// Send the exported data file
|
|
res.download(exportedFilePath, 'GhostData.json');
|
|
}).otherwise(function (error) {
|
|
// Notify of an error if it occurs
|
|
return api.notifications.browse().then(function (notifications) {
|
|
var 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);
|
|
});
|
|
});
|
|
});
|
|
},
|
|
'import': function (req, res) {
|
|
var notification,
|
|
databaseVersion;
|
|
|
|
if (!req.files.importfile || !req.files.importfile.path || req.files.importfile.name.indexOf('json') === -1) {
|
|
/**
|
|
* Notify of an error if it occurs
|
|
*
|
|
* - If there's no file (although if you don't select anything, the input is still submitted, so
|
|
* !req.files.importfile will always be false)
|
|
* - 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);
|
|
});
|
|
});
|
|
}
|
|
|
|
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);
|
|
}).then(function (fileContents) {
|
|
var importData,
|
|
error = '',
|
|
tableKeys = _.keys(schema);
|
|
|
|
// Parse the json data
|
|
try {
|
|
importData = JSON.parse(fileContents);
|
|
} catch (e) {
|
|
return when.reject(new Error("Failed to parse the import file"));
|
|
}
|
|
|
|
if (!importData.meta || !importData.meta.version) {
|
|
return when.reject(new Error("Import data does not specify version"));
|
|
}
|
|
|
|
_.each(tableKeys, function (constkey) {
|
|
_.each(importData.data[constkey], function (elem) {
|
|
var prop;
|
|
for (prop in elem) {
|
|
if (elem.hasOwnProperty(prop)) {
|
|
if (schema[constkey].hasOwnProperty(prop)) {
|
|
if (!_.isNull(elem[prop])) {
|
|
if (elem[prop].length > schema[constkey][prop].maxlength) {
|
|
error += error !== "" ? "<br>" : "";
|
|
error += "Property '" + prop + "' exceeds maximum length of " + schema[constkey][prop].maxlength + " (element:" + constkey + " / id:" + elem.id + ")";
|
|
}
|
|
} else {
|
|
if (!schema[constkey][prop].nullable) {
|
|
error += error !== "" ? "<br>" : "";
|
|
error += "Property '" + prop + "' is not nullable (element:" + constkey + " / id:" + elem.id + ")";
|
|
}
|
|
}
|
|
} else {
|
|
error += error !== "" ? "<br>" : "";
|
|
error += "Property '" + prop + "' is not allowed (element:" + constkey + " / id:" + elem.id + ")";
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
if (error !== "") {
|
|
return when.reject(new Error(error));
|
|
}
|
|
// Import for the current version
|
|
return dataImport(databaseVersion, importData);
|
|
|
|
}).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);
|
|
});
|
|
}).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);
|
|
});
|
|
});
|
|
});
|
|
},
|
|
'deleteAllContent': function () {
|
|
return when(dataProvider.deleteAllContent())
|
|
.then(function () {
|
|
return when.resolve({message: 'Successfully deleted all content from your blog.'});
|
|
}, function (error) {
|
|
return when.reject({errorCode: 500, message: error.message || error});
|
|
});
|
|
}
|
|
};
|
|
|
|
module.exports = db; |