mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-22 18:31:57 +03:00
c02ebb0dcf
closes #2610, refs #2697 - cleanup API index.js, and add docs - all API methods take consistent arguments: object & options - browse, read, destroy take options, edit and add take object and options - the context is passed as part of options, meaning no more .call everywhere - destroy expects an object, rather than an id all the way down to the model layer - route params such as :id, :slug, and :key are passed as an option & used to perform reads, updates and deletes where possible - settings / themes may need work here still - HTTP posts api can find a post by slug - Add API utils for checkData
263 lines
8.9 KiB
JavaScript
263 lines
8.9 KiB
JavaScript
var when = require('when'),
|
|
_ = require('lodash'),
|
|
models = require('../../models'),
|
|
Importer000,
|
|
updatedSettingKeys;
|
|
|
|
updatedSettingKeys = {
|
|
activePlugins: 'activeApps',
|
|
installedPlugins: 'installedApps'
|
|
};
|
|
|
|
|
|
Importer000 = function () {
|
|
_.bindAll(this, 'basicImport');
|
|
|
|
this.version = '000';
|
|
|
|
this.importFrom = {
|
|
'000': this.basicImport,
|
|
'001': this.basicImport,
|
|
'002': this.basicImport,
|
|
'003': this.basicImport
|
|
};
|
|
};
|
|
|
|
Importer000.prototype.importData = function (data) {
|
|
return this.canImport(data)
|
|
.then(function (importerFunc) {
|
|
return importerFunc(data);
|
|
}, function (reason) {
|
|
return when.reject(reason);
|
|
});
|
|
};
|
|
|
|
Importer000.prototype.canImport = function (data) {
|
|
if (data.meta && data.meta.version && this.importFrom[data.meta.version]) {
|
|
return when.resolve(this.importFrom[data.meta.version]);
|
|
}
|
|
|
|
return when.reject("Unsupported version of data: " + data.meta.version);
|
|
};
|
|
|
|
|
|
function stripProperties(properties, data) {
|
|
data = _.clone(data, true);
|
|
_.each(data, function (obj) {
|
|
_.each(properties, function (property) {
|
|
delete obj[property];
|
|
});
|
|
});
|
|
return data;
|
|
}
|
|
|
|
function preProcessPostTags(tableData) {
|
|
var postTags,
|
|
postsWithTags = {};
|
|
|
|
|
|
postTags = tableData.posts_tags;
|
|
_.each(postTags, function (post_tag) {
|
|
if (!postsWithTags.hasOwnProperty(post_tag.post_id)) {
|
|
postsWithTags[post_tag.post_id] = [];
|
|
}
|
|
postsWithTags[post_tag.post_id].push(post_tag.tag_id);
|
|
});
|
|
|
|
_.each(postsWithTags, function (tag_ids, post_id) {
|
|
var post, tags;
|
|
post = _.find(tableData.posts, function (post) {
|
|
return post.id === parseInt(post_id, 10);
|
|
});
|
|
if (post) {
|
|
tags = _.filter(tableData.tags, function (tag) {
|
|
return _.indexOf(tag_ids, tag.id) !== -1;
|
|
});
|
|
post.tags = [];
|
|
_.each(tags, function (tag) {
|
|
// names are unique.. this should get the right tags added
|
|
// as long as tags are added first;
|
|
post.tags.push({name: tag.name});
|
|
});
|
|
}
|
|
});
|
|
|
|
return tableData;
|
|
}
|
|
|
|
function importTags(ops, tableData, transaction) {
|
|
tableData = stripProperties(['id'], tableData);
|
|
_.each(tableData, function (tag) {
|
|
ops.push(models.Tag.findOne({name: tag.name}, {transacting: transaction}).then(function (_tag) {
|
|
if (!_tag) {
|
|
return models.Tag.add(tag, {user: 1, transacting: transaction})
|
|
// add pass-through error handling so that bluebird doesn't think we've dropped it
|
|
.otherwise(function (error) { return when.reject(error); });
|
|
}
|
|
return when.resolve(_tag);
|
|
}));
|
|
});
|
|
}
|
|
|
|
function importPosts(ops, tableData, transaction) {
|
|
tableData = stripProperties(['id'], tableData);
|
|
_.each(tableData, function (post) {
|
|
ops.push(models.Post.add(post, {user: 1, transacting: transaction, importing: true})
|
|
// add pass-through error handling so that bluebird doesn't think we've dropped it
|
|
.otherwise(function (error) { return when.reject(error); }));
|
|
});
|
|
}
|
|
|
|
function importUsers(ops, tableData, transaction) {
|
|
// don't override the users credentials
|
|
tableData = stripProperties(['id', 'email', 'password'], tableData);
|
|
tableData[0].id = 1;
|
|
ops.push(models.User.edit(tableData[0], {id: 1, user: 1, transacting: transaction})
|
|
// add pass-through error handling so that bluebird doesn't think we've dropped it
|
|
.otherwise(function (error) { return when.reject(error); }));
|
|
}
|
|
|
|
function importSettings(ops, tableData, transaction) {
|
|
// for settings we need to update individual settings, and insert any missing ones
|
|
// settings we MUST NOT update are 'core' and 'theme' settings
|
|
// as all of these will cause side effects which don't make sense for an import
|
|
var blackList = ['core', 'theme'];
|
|
|
|
tableData = stripProperties(['id'], tableData);
|
|
tableData = _.filter(tableData, function (data) {
|
|
return blackList.indexOf(data.type) === -1;
|
|
});
|
|
|
|
// Clean up legacy plugin setting references
|
|
_.each(tableData, function (datum) {
|
|
datum.key = updatedSettingKeys[datum.key] || datum.key;
|
|
});
|
|
|
|
ops.push(models.Settings.edit(tableData, {user: 1, transacting: transaction})
|
|
// add pass-through error handling so that bluebird doesn't think we've dropped it
|
|
.otherwise(function (error) { return when.reject(error); }));
|
|
}
|
|
|
|
function importApps(ops, tableData, transaction) {
|
|
tableData = stripProperties(['id'], tableData);
|
|
_.each(tableData, function (app) {
|
|
// Avoid duplicates
|
|
ops.push(models.App.findOne({name: app.name}, {transacting: transaction}).then(function (_app) {
|
|
if (!_app) {
|
|
return models.App.add(app, {transacting: transaction})
|
|
// add pass-through error handling so that bluebird doesn't think we've dropped it
|
|
.otherwise(function (error) { return when.reject(error); });
|
|
}
|
|
return when.resolve(_app);
|
|
}));
|
|
});
|
|
}
|
|
|
|
// function importAppSettings(ops, tableData, transaction) {
|
|
// var appsData = tableData.apps,
|
|
// appSettingsData = tableData.app_settings,
|
|
// appName;
|
|
//
|
|
// appSettingsData = stripProperties(['id'], appSettingsData);
|
|
//
|
|
// _.each(appSettingsData, function (appSetting) {
|
|
// // Find app to attach settings to
|
|
// appName = _.find(appsData, function (app) {
|
|
// return app.id === appSetting.app_id;
|
|
// }).name;
|
|
// ops.push(models.App.findOne({name: appName}, {transacting: transaction}).then(function (_app) {
|
|
// if (_app) {
|
|
// // Fix app_id
|
|
// appSetting.app_id = _app.id;
|
|
// return models.AppSetting.add(appSetting, {transacting: transaction})
|
|
// // add pass-through error handling so that bluebird doesn't think we've dropped it
|
|
// .otherwise(function (error) { return when.reject(error); });
|
|
// }
|
|
// // Gracefully ignore missing apps
|
|
// return when.resolve(_app);
|
|
// }));
|
|
// });
|
|
// }
|
|
|
|
// No data needs modifying, we just import whatever tables are available
|
|
Importer000.prototype.basicImport = function (data) {
|
|
var ops = [],
|
|
tableData = data.data;
|
|
return models.Base.transaction(function (t) {
|
|
|
|
// Do any pre-processing of relationships (we can't depend on ids)
|
|
if (tableData.posts_tags && tableData.posts && tableData.tags) {
|
|
tableData = preProcessPostTags(tableData);
|
|
}
|
|
|
|
// Import things in the right order:
|
|
if (tableData.tags && tableData.tags.length) {
|
|
importTags(ops, tableData.tags, t);
|
|
}
|
|
|
|
if (tableData.posts && tableData.posts.length) {
|
|
importPosts(ops, tableData.posts, t);
|
|
}
|
|
|
|
if (tableData.users && tableData.users.length) {
|
|
importUsers(ops, tableData.users, t);
|
|
}
|
|
|
|
if (tableData.settings && tableData.settings.length) {
|
|
importSettings(ops, tableData.settings, t);
|
|
}
|
|
|
|
if (tableData.apps && tableData.apps.length) {
|
|
importApps(ops, tableData.apps, t);
|
|
|
|
// ToDo: This is rather complicated
|
|
// Only import settings if there are apps defined
|
|
//if (tableData.app_settings && tableData.app_settings.length) {
|
|
// importAppSettings(ops, _.pick(tableData, 'apps', 'app_settings'), t);
|
|
//}
|
|
|
|
//if (tableData.app_fields && tableData.app_fields.length) {
|
|
// importAppFields(ops, _.pick(tableData, 'apps', 'posts', 'app_fields'), t);
|
|
//}
|
|
}
|
|
|
|
/** do nothing with these tables, the data shouldn't have changed from the fixtures
|
|
* permissions
|
|
* roles
|
|
* permissions_roles
|
|
* permissions_users
|
|
* roles_users
|
|
*/
|
|
|
|
// Write changes to DB, if successful commit, otherwise rollback
|
|
// when.all() does not work as expected, when.settle() does.
|
|
when.settle(ops).then(function (descriptors) {
|
|
var errors = [];
|
|
|
|
descriptors.forEach(function (d) {
|
|
if (d.state === 'rejected') {
|
|
errors = errors.concat(d.reason);
|
|
}
|
|
});
|
|
|
|
if (errors.length === 0) {
|
|
t.commit();
|
|
} else {
|
|
t.rollback(errors);
|
|
}
|
|
});
|
|
}).then(function () {
|
|
//TODO: could return statistics of imported items
|
|
return when.resolve();
|
|
}, function (error) {
|
|
return when.reject(error);
|
|
});
|
|
};
|
|
|
|
module.exports = {
|
|
Importer000: Importer000,
|
|
importData: function (data) {
|
|
return new Importer000().importData(data);
|
|
}
|
|
};
|