// # Fixtures // This module handles populating or updating fixtures. // // Currently fixtures only change between data version 002 and 003, therefore the update logic is hard coded // rather than abstracted into a migration system. The upgrade function checks that its changes are safe before // making them. var Promise = require('bluebird'), sequence = require('../../utils/sequence'), _ = require('lodash'), errors = require('../../errors'), config = require('../../config'), utils = require('../../utils'), models = require('../../models'), fixtures = require('./fixtures'), permissions = require('./permissions'), notifications = require('../../api/notifications'), // Private logInfo, to003, to004, convertAdminToOwner, createOwner, options = {context: {internal: true}}, // Public populate, update; logInfo = function logInfo(message) { errors.logInfo('Migrations', message); }; /** * Convert admin to Owner * Changes an admin user to have the owner role * @returns {Promise|*} */ convertAdminToOwner = function convertAdminToOwner() { var adminUser; return models.User.findOne({role: 'Administrator'}).then(function (user) { adminUser = user; return models.Role.findOne({name: 'Owner'}); }).then(function (ownerRole) { if (adminUser) { logInfo('Converting admin to owner'); return adminUser.roles().updatePivot({role_id: ownerRole.id}); } }); }; /** * Create Owner * Creates the user fixture and gives it the owner role * @returns {Promise|*} */ createOwner = function createOwner() { var user = fixtures.users[0]; return models.Role.findOne({name: 'Owner'}).then(function (ownerRole) { user.roles = [ownerRole.id]; user.password = utils.uid(50); logInfo('Creating owner'); return models.User.add(user, options); }); }; populate = function populate() { var ops = [], relations = [], Post = models.Post, Tag = models.Tag, Role = models.Role, Client = models.Client; logInfo('Populating fixtures'); _.each(fixtures.posts, function (post) { ops.push(Post.add(post, options)); }); _.each(fixtures.tags, function (tag) { ops.push(Tag.add(tag, options)); }); _.each(fixtures.roles, function (role) { ops.push(Role.add(role, options)); }); _.each(fixtures.clients, function (client) { ops.push(Client.add(client, options)); }); // add the tag to the post relations.push(function () { return Post.forge({slug: fixtures.posts[0].slug}).fetch().then(function (post) { return Tag.forge({slug: fixtures.tags[0].slug}).fetch().then(function (tag) { return post.related('tags').attach(tag.id); }); }); }); return Promise.all(ops).then(function () { return sequence(relations); }).then(function () { return permissions.populate(options); }).then(function () { return createOwner(); }).catch(function (errs) { errors.logError(errs); }); }; /** * ### Update fixtures to 003 * Need to add client & owner role, then update permissions to 003 as well * By doing this in a way that checks before adding, we can ensure that it's possible to force a migration safely. * * Note: At the moment this is pretty adhoc & untestable, in future it would be better to have a config based system. * @returns {Promise|*} */ to003 = function to003() { var ops = [], upgradeOp, Role = models.Role, Client = models.Client; logInfo('Upgrading fixtures to 003'); // Add the client fixture if missing upgradeOp = Client.findOne({slug: fixtures.clients[0].slug}).then(function (client) { if (!client) { logInfo('Adding ghost-admin client fixture'); return Client.add(fixtures.clients[0], options); } }); ops.push(upgradeOp); // Add the owner role if missing upgradeOp = Role.findOne({name: fixtures.roles[3].name}).then(function (owner) { if (!owner) { logInfo('Adding owner role fixture'); _.each(fixtures.roles.slice(3), function (role) { return Role.add(role, options); }); } }); ops.push(upgradeOp); return Promise.all(ops).then(function () { return permissions.to003(options); }).then(function () { return convertAdminToOwner(); }); }; /** * Update ghost_foot to include a CDN of jquery if the DB is migrating from * @return {Promise} */ to004 = function to004() { var value, ops = [], upgradeOp, jquery = [ '\n', '\n\n' ], privacyMessage = [ 'jQuery has been removed from Ghost core and is now being loaded from the jQuery Foundation\'s CDN.', 'This can be changed or removed in your Code Injection settings area.' ]; logInfo('Upgrading fixtures to 004'); // add jquery setting and privacy info upgradeOp = models.Settings.findOne('ghost_foot').then(function (setting) { if (setting) { value = setting.attributes.value; // Only add jQuery if it's not already in there if (value.indexOf(jquery.join('')) === -1) { logInfo('Adding jQuery link to ghost_foot'); value = jquery.join('') + value; return models.Settings.edit({key: 'ghost_foot', value: value}, options).then(function () { if (_.isEmpty(config.privacy)) { return Promise.resolve(); } logInfo(privacyMessage.join(' ').replace(/<\/?strong>/g, '')); return notifications.add({notifications: [{ type: 'info', message: privacyMessage.join(' ') }]}, options); }); } } }); ops.push(upgradeOp); // change `type` for protected blog `isPrivate` setting upgradeOp = models.Settings.findOne('isPrivate').then(function (setting) { if (setting) { logInfo('Update isPrivate setting'); return models.Settings.edit({key: 'isPrivate', type: 'private'}, options); } return Promise.resolve(); }); ops.push(upgradeOp); // change `type` for protected blog `password` setting upgradeOp = models.Settings.findOne('password').then(function (setting) { if (setting) { logInfo('Update password setting'); return models.Settings.edit({key: 'password', type: 'private'}, options); } return Promise.resolve(); }); ops.push(upgradeOp); // Update ghost-admin client fixture // ghost-admin should exist from 003 version upgradeOp = models.Client.findOne({slug: fixtures.clients[0].slug}).then(function (client) { if (client) { logInfo('Update ghost-admin client fixture'); return models.Client.edit(fixtures.clients[0], _.extend({}, options, {id: client.id})); } return Promise.resolve(); }); ops.push(upgradeOp); // add ghost-frontend client if missing upgradeOp = models.Client.findOne({slug: fixtures.clients[1].slug}).then(function (client) { if (!client) { logInfo('Add ghost-frontend client fixture'); return models.Client.add(fixtures.clients[1], options); } return Promise.resolve(); }); ops.push(upgradeOp); // add ghost-frontend client if missing upgradeOp = models.Tag.findAll(options).then(function (tags) { var tagOps = []; if (tags) { tags.each(function (tag) { var name = tag.get('name'), updated = name.replace(/^(,+)/, '').trim(); // If we've ended up with an empty string, default to just 'tag' updated = updated === '' ? 'tag' : updated; if (name !== updated) { tagOps.push(tag.save({name: updated}, options)); } }); if (tagOps.length > 0) { logInfo('Cleaning ' + tagOps.length + ' malformed tags'); return Promise.all(tagOps); } } return Promise.resolve(); }); ops.push(upgradeOp); return Promise.all(ops); }; update = function update(fromVersion, toVersion) { var ops = []; logInfo('Updating fixtures'); // Are we migrating to, or past 003? if ((fromVersion < '003' && toVersion >= '003') || fromVersion === '003' && toVersion === '003' && process.env.FORCE_MIGRATION) { ops.push(to003); } if (fromVersion < '004' && toVersion === '004' || fromVersion === '004' && toVersion === '004' && process.env.FORCE_MIGRATION) { ops.push(to004); } return sequence(ops); }; module.exports = { populate: populate, update: update };