mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-17 21:41:49 +03:00
2d2e61676d
fixes #3716 - change the importer to not override any user details - only set published_by if it is not already set - import users before anything else - process the import and map user ids to existing users - test fix - owner should have owner role - test fix - catch invalid success in importer
259 lines
9.8 KiB
JavaScript
259 lines
9.8 KiB
JavaScript
var when = require('when'),
|
|
_ = require('lodash'),
|
|
models = require('../../models'),
|
|
errors = require('../../errors'),
|
|
globalUtils = require('../../utils'),
|
|
|
|
internal = {context: {internal: true}},
|
|
utils,
|
|
areEmpty,
|
|
updatedSettingKeys,
|
|
stripProperties;
|
|
|
|
updatedSettingKeys = {
|
|
activePlugins: 'activeApps',
|
|
installedPlugins: 'installedApps'
|
|
};
|
|
|
|
areEmpty = function (object) {
|
|
var fields = _.toArray(arguments).slice(1),
|
|
areEmpty = _.all(fields, function (field) {
|
|
return _.isEmpty(object[field]);
|
|
});
|
|
|
|
return areEmpty;
|
|
};
|
|
|
|
stripProperties = function stripProperties(properties, data) {
|
|
data = _.clone(data, true);
|
|
_.each(data, function (obj) {
|
|
_.each(properties, function (property) {
|
|
delete obj[property];
|
|
});
|
|
});
|
|
return data;
|
|
};
|
|
|
|
utils = {
|
|
processUsers: function preProcessUsers(tableData, owner, existingUsers, objs) {
|
|
// We need to:
|
|
// 1. figure out who the owner of the blog is
|
|
// 2. figure out what users we have
|
|
// 3. figure out what users the import data refers to in foreign keys
|
|
// 4. try to map each one to a user
|
|
var userKeys = ['created_by', 'updated_by', 'published_by', 'author_id'],
|
|
userMap = {};
|
|
|
|
// Search the passed in objects for any user foreign keys
|
|
_.each(objs, function (obj) {
|
|
if (tableData[obj]) {
|
|
// For each object in the tableData that matches
|
|
_.each(tableData[obj], function (data) {
|
|
//console.log('checking ' + obj + ' ' + data.slug);
|
|
// For each possible user foreign key
|
|
_.each(userKeys, function (key) {
|
|
if (_.has(data, key) && data[key] !== null) {
|
|
//console.log('found ' + key + ' with value ' + data[key]);
|
|
userMap[data[key]] = {};
|
|
}
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
// We now have a list of users we need to figure out what their email addresses are
|
|
_.each(_.keys(userMap), function (userToMap) {
|
|
userToMap = parseInt(userToMap, 10);
|
|
var foundUser = _.find(tableData.users, function (tableDataUser) {
|
|
return tableDataUser.id === userToMap;
|
|
});
|
|
|
|
// we now know that userToMap's email is foundUser.email - look them up in existing users
|
|
if (foundUser && _.has(foundUser, 'email') && _.has(existingUsers, foundUser.email)) {
|
|
existingUsers[foundUser.email].importId = userToMap;
|
|
userMap[userToMap] = existingUsers[foundUser.email].realId;
|
|
} else if (userToMap === 1) {
|
|
// if we don't have user data and the id is 1, we assume this means the owner
|
|
existingUsers[owner.email].importId = userToMap;
|
|
userMap[userToMap] = existingUsers[owner.email].realId;
|
|
} else {
|
|
throw new errors.DataImportError(
|
|
'Attempting to import data linked to unknown user id ' + userToMap, 'user.id', userToMap
|
|
);
|
|
}
|
|
});
|
|
|
|
// now replace any user foreign keys
|
|
_.each(objs, function (obj) {
|
|
if (tableData[obj]) {
|
|
// For each object in the tableData that matches
|
|
_.each(tableData[obj], function (data) {
|
|
// For each possible user foreign key
|
|
_.each(userKeys, function (key) {
|
|
if (_.has(data, key) && data[key] !== null) {
|
|
data[key] = userMap[data[key]];
|
|
}
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
return tableData;
|
|
},
|
|
|
|
preProcessPostTags: 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;
|
|
},
|
|
|
|
preProcessRolesUsers: function preProcessRolesUsers(tableData) {
|
|
_.each(tableData.roles_users, function (role_user) {
|
|
var user = _.find(tableData.users, function (user) {
|
|
return user.id === parseInt(role_user.user_id, 10);
|
|
});
|
|
// just the one role for now
|
|
if (user && !user.roles) {
|
|
user.roles = [role_user.role_id];
|
|
}
|
|
});
|
|
|
|
return tableData;
|
|
},
|
|
|
|
importTags: function importTags(ops, tableData, transaction) {
|
|
tableData = stripProperties(['id'], tableData);
|
|
_.each(tableData, function (tag) {
|
|
// Validate minimum tag fields
|
|
if (areEmpty(tag, 'name', 'slug')) {
|
|
return;
|
|
}
|
|
|
|
ops.push(models.Tag.findOne({name: tag.name}, {transacting: transaction}).then(function (_tag) {
|
|
if (!_tag) {
|
|
return models.Tag.add(tag, _.extend(internal, {transacting: transaction}))
|
|
// add pass-through error handling so that bluebird doesn't think we've dropped it
|
|
.catch(function (error) {
|
|
return when.reject({raw: error, model: 'tag', data: tag});
|
|
});
|
|
}
|
|
return when.resolve(_tag);
|
|
}));
|
|
});
|
|
},
|
|
|
|
importPosts: function importPosts(ops, tableData, transaction) {
|
|
tableData = stripProperties(['id'], tableData);
|
|
_.each(tableData, function (post) {
|
|
// Validate minimum post fields
|
|
if (areEmpty(post, 'title', 'slug', 'markdown')) {
|
|
return;
|
|
}
|
|
ops.push(models.Post.add(post, _.extend(internal, {transacting: transaction, importing: true}))
|
|
// add pass-through error handling so that bluebird doesn't think we've dropped it
|
|
.catch(function (error) {
|
|
return when.reject({raw: error, model: 'post', data: post});
|
|
}));
|
|
});
|
|
},
|
|
|
|
importUsers: function importUsers(tableData, existingUsers, transaction) {
|
|
var ops = [];
|
|
|
|
tableData = stripProperties(['id'], tableData);
|
|
_.each(tableData, function (user) {
|
|
// Validate minimum user fields
|
|
if (areEmpty(user, 'name', 'slug', 'email')) {
|
|
return;
|
|
}
|
|
|
|
if (_.has(existingUsers, user.email)) {
|
|
// User is already present, ignore
|
|
return;
|
|
}
|
|
|
|
// Set password to a random password, and lock the account
|
|
user.password = globalUtils.uid(50);
|
|
user.status = 'locked';
|
|
|
|
ops.push(models.User.add(user, _.extend(internal, {transacting: transaction}))
|
|
// add pass-through error handling so that bluebird doesn't think we've dropped it
|
|
.catch(function (error) {
|
|
return when.reject({raw: error, model: 'user', data: user});
|
|
}));
|
|
});
|
|
|
|
return ops;
|
|
},
|
|
|
|
importSettings: 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, _.extend(internal, {transacting: transaction}))
|
|
// add pass-through error handling so that bluebird doesn't think we've dropped it
|
|
.catch(function (error) {
|
|
return when.reject({raw: error, model: 'setting', data: tableData});
|
|
}));
|
|
},
|
|
|
|
/** For later **/
|
|
importApps: 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, _.extend(internal, {transacting: transaction}))
|
|
// add pass-through error handling so that bluebird doesn't think we've dropped it
|
|
.catch(function (error) {
|
|
return when.reject({raw: error, model: 'app', data: app});
|
|
});
|
|
}
|
|
return when.resolve(_app);
|
|
}));
|
|
});
|
|
}
|
|
};
|
|
|
|
module.exports = utils; |