diff --git a/core/server/models/base/index.js b/core/server/models/base/index.js index 8f53ca1c68..6350044af0 100644 --- a/core/server/models/base/index.js +++ b/core/server/models/base/index.js @@ -136,6 +136,8 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({ * @returns {*} */ onValidate: function onValidate(model, columns, options) { + this.setEmptyValuesToNull(); + return validation.validateSchema(this.tableName, this, options); }, @@ -288,6 +290,24 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({ return attrs; }, + // Sets given values to `null` + setEmptyValuesToNull: function setEmptyValuesToNull() { + var self = this, + attr; + + if (!this.emptyStringProperties) { + return; + } + + attr = this.emptyStringProperties(); + + _.each(attr, function (value) { + if (self.get(value) === '') { + self.set(value, null); + } + }); + }, + // Get the user from the options object contextUser: function contextUser(options) { options = options || {}; diff --git a/core/server/models/post.js b/core/server/models/post.js index cf3d08cc8e..8d1a39cd73 100644 --- a/core/server/models/post.js +++ b/core/server/models/post.js @@ -302,6 +302,13 @@ Post = ghostBookshelf.Model.extend({ return sequence(ops); }, + emptyStringProperties: function emptyStringProperties() { + // CASE: the client might send empty image properties with "" instead of setting them to null. + // This can cause GQL to fail. We therefore enforce 'null' for empty image properties. + // See https://github.com/TryGhost/GQL/issues/24 + return ['feature_image', 'og_image', 'twitter_image']; + }, + onCreating: function onCreating(model, attr, options) { options = options || {}; diff --git a/core/server/models/tag.js b/core/server/models/tag.js index e9d667541c..cc8d316584 100644 --- a/core/server/models/tag.js +++ b/core/server/models/tag.js @@ -49,6 +49,13 @@ Tag = ghostBookshelf.Model.extend({ } }, + emptyStringProperties: function emptyStringProperties() { + // CASE: the client might send empty image properties with "" instead of setting them to null. + // This can cause GQL to fail. We therefore enforce 'null' for empty image properties. + // See https://github.com/TryGhost/GQL/issues/24 + return ['feature_image']; + }, + posts: function posts() { return this.belongsToMany('Post'); }, diff --git a/core/server/models/user.js b/core/server/models/user.js index 1cd58b2e7b..14eecd5cb9 100644 --- a/core/server/models/user.js +++ b/core/server/models/user.js @@ -211,6 +211,13 @@ User = ghostBookshelf.Model.extend({ return attrs; }, + emptyStringProperties: function emptyStringProperties() { + // CASE: the client might send empty image properties with "" instead of setting them to null. + // This can cause GQL to fail. We therefore enforce 'null' for empty image properties. + // See https://github.com/TryGhost/GQL/issues/24 + return ['profile_image', 'cover_image']; + }, + format: function format(options) { if (!_.isEmpty(options.website) && !validator.isURL(options.website, { diff --git a/core/test/unit/models/base/index_spec.js b/core/test/unit/models/base/index_spec.js new file mode 100644 index 0000000000..a9c43b3d8b --- /dev/null +++ b/core/test/unit/models/base/index_spec.js @@ -0,0 +1,42 @@ +'use strict'; + +var should = require('should'), + sinon = require('sinon'), + _ = require('lodash'), + models = require('../../../../server/models'), + ghostBookshelf, + testUtils = require('../../../utils'), + + sandbox = sinon.sandbox.create(); + +describe('Models: base', function () { + before(function () { + models.init(); + }); + + afterEach(function () { + sandbox.restore(); + }); + + describe('setEmptyValuesToNull', function () { + it('resets given empty value to null', function () { + const base = models.Base.Model.forge({a: '', b: ''}); + + base.emptyStringProperties = sandbox.stub(); + base.emptyStringProperties.returns(['a']); + + base.get('a').should.eql(''); + base.get('b').should.eql(''); + base.setEmptyValuesToNull(); + should.not.exist(base.get('a')); + base.get('b').should.eql(''); + }); + + it('does not reset to null if model does\'t provide properties', function () { + const base = models.Base.Model.forge({a: ''}); + base.get('a').should.eql(''); + base.setEmptyValuesToNull(); + base.get('a').should.eql(''); + }); + }); +}); diff --git a/core/test/unit/models/post_spec.js b/core/test/unit/models/post_spec.js index eb827828dd..4a8a7a7626 100644 --- a/core/test/unit/models/post_spec.js +++ b/core/test/unit/models/post_spec.js @@ -1,16 +1,49 @@ +'use strict'; + var should = require('should'), // jshint ignore:line sinon = require('sinon'), models = require('../../../server/models'), common = require('../../../server/lib/common'), - utils = require('../../utils'), - + testUtils = require('../../utils'), sandbox = sinon.sandbox.create(); -describe('Models: Post', function () { +describe('Unit: models/post', function () { before(function () { models.init(); }); + after(function () { + sandbox.restore(); + }); + + describe('Edit', function () { + let knexMock; + + before(function () { + knexMock = new testUtils.mocks.knex(); + knexMock.mock(); + }); + + after(function () { + knexMock.unmock(); + }); + + it('resets given empty value to null', function () { + return models.Post.findOne({slug: 'html-ipsum'}) + .then(function (post) { + post.get('slug').should.eql('html-ipsum'); + post.get('feature_image').should.eql('https://example.com/super_photo.jpg'); + post.set('feature_image', ''); + post.set('custom_excerpt', ''); + return post.save(); + }) + .then(function (post) { + should(post.get('feature_image')).be.null(); + post.get('custom_excerpt').should.eql(''); + }); + }); + }); + describe('Permissible', function () { describe('As Contributor', function () { describe('Editing', function () { @@ -29,7 +62,7 @@ describe('Models: Post', function () { 'edit', context, unsafeAttrs, - utils.permissions.contributor, + testUtils.permissions.contributor, false, false ).then(() => { @@ -56,7 +89,7 @@ describe('Models: Post', function () { 'edit', context, unsafeAttrs, - utils.permissions.contributor, + testUtils.permissions.contributor, false, true ).then(() => { @@ -83,7 +116,7 @@ describe('Models: Post', function () { 'edit', context, unsafeAttrs, - utils.permissions.contributor, + testUtils.permissions.contributor, false, true ).then(() => { @@ -110,7 +143,7 @@ describe('Models: Post', function () { 'edit', context, unsafeAttrs, - utils.permissions.contributor, + testUtils.permissions.contributor, false, true ).then(() => { @@ -137,7 +170,7 @@ describe('Models: Post', function () { 'edit', context, unsafeAttrs, - utils.permissions.contributor, + testUtils.permissions.contributor, false, true ).then((result) => { @@ -161,7 +194,7 @@ describe('Models: Post', function () { 'add', context, unsafeAttrs, - utils.permissions.contributor, + testUtils.permissions.contributor, false, true ).then(() => { @@ -185,7 +218,7 @@ describe('Models: Post', function () { 'add', context, unsafeAttrs, - utils.permissions.contributor, + testUtils.permissions.contributor, false, true ).then(() => { @@ -209,7 +242,7 @@ describe('Models: Post', function () { 'add', context, unsafeAttrs, - utils.permissions.contributor, + testUtils.permissions.contributor, false, true ).then((result) => { @@ -234,7 +267,7 @@ describe('Models: Post', function () { 'destroy', context, {}, - utils.permissions.contributor, + testUtils.permissions.contributor, false, true ).then(() => { @@ -260,7 +293,7 @@ describe('Models: Post', function () { 'destroy', context, {}, - utils.permissions.contributor, + testUtils.permissions.contributor, false, true ).then(() => { @@ -286,7 +319,7 @@ describe('Models: Post', function () { 'destroy', context, {}, - utils.permissions.contributor, + testUtils.permissions.contributor, false, true ).then((result) => { @@ -314,7 +347,7 @@ describe('Models: Post', function () { 'edit', context, unsafeAttrs, - utils.permissions.author, + testUtils.permissions.author, false, true ).then(() => { @@ -340,7 +373,7 @@ describe('Models: Post', function () { 'edit', context, unsafeAttrs, - utils.permissions.author, + testUtils.permissions.author, false, true ).then(() => { @@ -366,7 +399,7 @@ describe('Models: Post', function () { 'edit', context, unsafeAttrs, - utils.permissions.author, + testUtils.permissions.author, false, true ).then(() => { @@ -388,7 +421,7 @@ describe('Models: Post', function () { 'add', context, unsafeAttrs, - utils.permissions.author, + testUtils.permissions.author, false, true ).then(() => { @@ -412,7 +445,7 @@ describe('Models: Post', function () { 'add', context, unsafeAttrs, - utils.permissions.author, + testUtils.permissions.author, false, true ).then(() => { @@ -437,7 +470,7 @@ describe('Models: Post', function () { 'edit', context, unsafeAttrs, - utils.permissions.editor, + testUtils.permissions.editor, false, true ).then(() => { @@ -463,7 +496,7 @@ describe('Models: Post', function () { 'edit', context, unsafeAttrs, - utils.permissions.editor, + testUtils.permissions.editor, true, true ).then(() => { diff --git a/core/test/unit/models/tag_spec.js b/core/test/unit/models/tag_spec.js new file mode 100644 index 0000000000..b5ccccddb6 --- /dev/null +++ b/core/test/unit/models/tag_spec.js @@ -0,0 +1,45 @@ +'use strict'; + +var should = require('should'), // jshint ignore:line + sinon = require('sinon'), + models = require('../../../server/models'), + testUtils = require('../../utils'), + sandbox = sinon.sandbox.create(); + +describe('Unit: models/tags', function () { + before(function () { + models.init(); + }); + + after(function () { + sandbox.restore(); + }); + + describe('Edit', function () { + let knexMock; + + before(function () { + knexMock = new testUtils.mocks.knex(); + knexMock.mock(); + }); + + after(function () { + knexMock.unmock(); + }); + + it('resets given empty value to null', function () { + return models.Tag.findOne({slug: 'kitchen-sink'}) + .then(function (tag) { + tag.get('slug').should.eql('kitchen-sink'); + tag.get('feature_image').should.eql('https://example.com/super_photo.jpg'); + tag.set('feature_image', ''); + tag.set('description', ''); + return tag.save(); + }) + .then(function (tag) { + should(tag.get('feature_image')).be.null(); + tag.get('description').should.eql(''); + }); + }); + }); +}); diff --git a/core/test/unit/models/user_spec.js b/core/test/unit/models/user_spec.js index 43f3997732..71f2ba23ee 100644 --- a/core/test/unit/models/user_spec.js +++ b/core/test/unit/models/user_spec.js @@ -308,4 +308,40 @@ describe('Unit: models/user', function () { }); }); }); + + describe('Edit', function () { + let knexMock; + + before(function () { + models.init(); + }); + + after(function () { + sandbox.restore(); + }); + + before(function () { + knexMock = new testUtils.mocks.knex(); + knexMock.mock(); + }); + + after(function () { + knexMock.unmock(); + }); + + it('resets given empty value to null', function () { + return models.User.findOne({slug: 'joe-bloggs'}) + .then(function (user) { + user.get('slug').should.eql('joe-bloggs'); + user.get('profile_image').should.eql('https://example.com/super_photo.jpg'); + user.set('profile_image', ''); + user.set('bio', ''); + return user.save(); + }) + .then(function (user) { + should(user.get('profile_image')).be.null(); + user.get('bio').should.eql(''); + }); + }); + }); }); diff --git a/core/test/utils/fixtures/data-generator.js b/core/test/utils/fixtures/data-generator.js index 653420557d..b98f5530b4 100644 --- a/core/test/utils/fixtures/data-generator.js +++ b/core/test/utils/fixtures/data-generator.js @@ -28,66 +28,67 @@ DataGenerator.Content = { posts: [ { id: ObjectId.generate(), - title: "HTML Ipsum", - slug: "html-ipsum", - mobiledoc: DataGenerator.markdownToMobiledoc("

HTML Ipsum Presents

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

Header Level 2

  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. Aliquam tincidunt mauris eu risus.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.

Header Level 3

#header h1 a{display: block;width: 300px;height: 80px;}
"), - published_at: new Date("2015-01-01"), - custom_excerpt: 'This is my custom excerpt!' + title: 'HTML Ipsum', + slug: 'html-ipsum', + mobiledoc: DataGenerator.markdownToMobiledoc('

HTML Ipsum Presents

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

Header Level 2

  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. Aliquam tincidunt mauris eu risus.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.

Header Level 3

#header h1 a{display: block;width: 300px;height: 80px;}
'), + published_at: new Date('2015-01-01'), + custom_excerpt: 'This is my custom excerpt!', + feature_image: 'https://example.com/super_photo.jpg' }, { id: ObjectId.generate(), - title: "Ghostly Kitchen Sink", - slug: "ghostly-kitchen-sink", - mobiledoc: DataGenerator.markdownToMobiledoc("

HTML Ipsum Presents

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

Header Level 2

  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. Aliquam tincidunt mauris eu risus.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.

Header Level 3

#header h1 a{display: block;width: 300px;height: 80px;}
"), - published_at: new Date("2015-01-02") + title: 'Ghostly Kitchen Sink', + slug: 'ghostly-kitchen-sink', + mobiledoc: DataGenerator.markdownToMobiledoc('

HTML Ipsum Presents

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

Header Level 2

  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. Aliquam tincidunt mauris eu risus.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.

Header Level 3

#header h1 a{display: block;width: 300px;height: 80px;}
'), + published_at: new Date('2015-01-02') }, { id: ObjectId.generate(), - title: "Short and Sweet", - slug: "short-and-sweet", - mobiledoc: DataGenerator.markdownToMobiledoc("## testing\n\nmctesters\n\n- test\n- line\n- items"), - html: "

testing

\n

mctesters

\n\n
", - feature_image: "http://placekitten.com/500/200", - meta_description: "test stuff", - published_at: new Date("2015-01-03"), - uuid: "2ac6b4f6-e1f3-406c-9247-c94a0496d39d" + title: 'Short and Sweet', + slug: 'short-and-sweet', + mobiledoc: DataGenerator.markdownToMobiledoc('## testing\n\nmctesters\n\n- test\n- line\n- items'), + html: '

testing

\n

mctesters

\n\n
', + feature_image: 'http://placekitten.com/500/200', + meta_description: 'test stuff', + published_at: new Date('2015-01-03'), + uuid: '2ac6b4f6-e1f3-406c-9247-c94a0496d39d' }, { id: ObjectId.generate(), - title: "Not finished yet", - slug: "unfinished", - mobiledoc: DataGenerator.markdownToMobiledoc("

HTML Ipsum Presents

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

Header Level 2

  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. Aliquam tincidunt mauris eu risus.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.

Header Level 3

#header h1 a{display: block;width: 300px;height: 80px;}
"), - status: "draft", - uuid: "d52c42ae-2755-455c-80ec-70b2ec55c903", + title: 'Not finished yet', + slug: 'unfinished', + mobiledoc: DataGenerator.markdownToMobiledoc('

HTML Ipsum Presents

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

Header Level 2

  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. Aliquam tincidunt mauris eu risus.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.

Header Level 3

#header h1 a{display: block;width: 300px;height: 80px;}
'), + status: 'draft', + uuid: 'd52c42ae-2755-455c-80ec-70b2ec55c903', featured: false }, { id: ObjectId.generate(), - title: "Not so short, bit complex", - slug: "not-so-short-bit-complex", - mobiledoc: DataGenerator.markdownToMobiledoc("

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.

1234
abcd
efgh
ijkl
Definition list
Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Lorem ipsum dolor sit amet
Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

") + title: 'Not so short, bit complex', + slug: 'not-so-short-bit-complex', + mobiledoc: DataGenerator.markdownToMobiledoc('

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.

1234
abcd
efgh
ijkl
Definition list
Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Lorem ipsum dolor sit amet
Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

') }, { id: ObjectId.generate(), - title: "This is a static page", - slug: "static-page-test", - mobiledoc: DataGenerator.markdownToMobiledoc("

Static page test is what this is for.

Hopefully you don't find it a bore.

"), + title: 'This is a static page', + slug: 'static-page-test', + mobiledoc: DataGenerator.markdownToMobiledoc('

Static page test is what this is for.

Hopefully you don\'t find it a bore.

'), page: 1 }, { id: ObjectId.generate(), - title: "This is a draft static page", - slug: "static-page-draft", - mobiledoc: DataGenerator.markdownToMobiledoc("

Static page test is what this is for.

Hopefully you don't find it a bore.

"), + title: 'This is a draft static page', + slug: 'static-page-draft', + mobiledoc: DataGenerator.markdownToMobiledoc('

Static page test is what this is for.

Hopefully you don\'t find it a bore.

'), page: 1, - status: "draft" + status: 'draft' }, { id: ObjectId.generate(), - title: "This is a scheduled post!!", - slug: "scheduled-post", - mobiledoc: DataGenerator.markdownToMobiledoc("

Welcome to my invisible post!

"), - status: "scheduled", + title: 'This is a scheduled post!!', + slug: 'scheduled-post', + mobiledoc: DataGenerator.markdownToMobiledoc('

Welcome to my invisible post!

'), + status: 'scheduled', published_at: moment().add(2, 'days').toDate() } ], @@ -95,28 +96,29 @@ DataGenerator.Content = { tags: [ { id: ObjectId.generate(), - name: "kitchen sink", - slug: "kitchen-sink" + name: 'kitchen sink', + slug: 'kitchen-sink', + feature_image: 'https://example.com/super_photo.jpg' }, { id: ObjectId.generate(), - name: "bacon", - slug: "bacon" + name: 'bacon', + slug: 'bacon' }, { id: ObjectId.generate(), - name: "chorizo", - slug: "chorizo" + name: 'chorizo', + slug: 'chorizo' }, { id: ObjectId.generate(), - name: "pollo", - slug: "pollo" + name: 'pollo', + slug: 'pollo' }, { id: ObjectId.generate(), - name: "injection", - slug: "injection" + name: 'injection', + slug: 'injection' } ], @@ -128,7 +130,8 @@ DataGenerator.Content = { name: 'Joe Bloggs', slug: 'joe-bloggs', email: 'jbloggs@example.com', - password: '$2b$10$ujPIlqjTsYwfc2/zrqZXZ.yd7cQQm2iOkAFenTAJfveKkc23nwdeS' + password: '$2b$10$ujPIlqjTsYwfc2/zrqZXZ.yd7cQQm2iOkAFenTAJfveKkc23nwdeS', + profile_image: 'https://example.com/super_photo.jpg' }, { // admin