From 80fbfd7a859e1ee283265da58c36cc4f4510f1cd Mon Sep 17 00:00:00 2001 From: Kevin Ansfield Date: Fri, 16 Aug 2019 17:46:00 +0100 Subject: [PATCH] Revert post.page->post.type handling no issue - the column addition/removal can be too slow for large sites - will be added back in 3.0 --- Revert "Fixed canary api for page/type column" This reverts commit a5a7e7e919d83af3ea9cd7402a75dff60f2d7e9c. Revert "Updated frontend canary url config for page/type" This reverts commit 19100ec5e6edbe67464c4938521fe25d7ec15041. Revert "Updated canary api to handle type column correctly (#11006)" This reverts commit c3e8ba0523f5460662dcd3cddf0affd337b26eba. Revert "Ensured `page` filter works in routes.yaml" This reverts commit 9037c19e50c4da026f4b797413a682e1411b032f. Revert "Replaced usage of mongo util with nql-map-key-values" This reverts commit 8c5f1d0ef0ad9a03fb3e362c31063e47a0173411. Revert "Added shared nql-map-key-values module" This reverts commit ef4fd4b8ef3824290a00a371dad5a505431ab689. Revert "Ensured page prop is present on content api response" This reverts commit cfa0a0862bf7b247cfeb9b689cfdf6b8e3fb0c10. Revert "Fixed failing regression tests" This reverts commit 9c2bb3811fba8ea127b22f13d21112dcf1f5a46d. Revert "Updated xmlrpc and slack service to use type column" This reverts commit 44a02c7d3635967dd3fe8f96b793b50fd398bd40. Revert "Updated v0.1 posts api to work with type column" This reverts commit 2c81d7c914ac0a2c3b7f1d6d0385479e61d15f18. Revert "Removed updates to v0.1 specific code" This reverts commit 08d83c1f5332b7db6b96814651b496e707c2e124. Revert "Added missing context from ValidationError" This reverts commit cd45ab4f54abefeee8605df84cfc864fff1ad385. Revert "Renamed page->type in the page&posts serializers" This reverts commit df99e724e3d7dc1665916844983849494deea80d. Revert "Added mongo helper to input serializers" This reverts commit fb8eadb4a8109ba987d79decfe331c669a446609. Revert "Passed mongoTransformer through to NQL" This reverts commit 0ae3f0fdfc864dcf5c90c6b56cf975997974742c. Revert "Permitted mongoTransformer option for read methods" This reverts commit a89376bf2618520626d2cf1b8d86f3c8c453db23. Revert "Updated the count plugin to reference the type column" This reverts commit a52f15d3d3503bc9ce4e20961c1f4a0fd49316c7. Revert "Updated hashes for db integrity check" This reverts commit bb6b337be3d30e919e4edfdc2e59182cb81e9e5d. Revert "Remove page column and remaining references" This reverts commit 9d7190d69255ac011848c6bf654886be81abeedc. Revert "Added type column to data generator" This reverts commit e59806cb45c47e0bd547801de54ac5332913fbf5. Revert "Removed references to page column in rss tests" This reverts commit 04d0f855dede1a1bd910c1bc7ca4913ae27472ae. Revert "Removed page column references in validation tests" This reverts commit f0afbc5cc06449ccae034b930709e29133ca8374. Revert "Updated the post model to use the `type` column" This reverts commit 1189bc823ac6adde4f25d63d9fc83ca94e38d672. Revert "Updated url service to use type column" This reverts commit 61612ba8fd38a72d8ef6af5b2c199a9dcd80b80b. Revert "Updated the v2 api to deal with type column" This reverts commit 57afb2de2baf702575a2ea3e4d8e1b914f769e00. Revert "Added type property to post model defaults" This reverts commit dc3345b1c59d03261ecd678a9cbad0ec91ef5a38. Revert "Added type property to the default post fixtures" This reverts commit 82d8c380336b6455ad09622edf7ecd803d0cbe23. Revert "Added type column to posts table" This reverts commit 9b85fc6a69363c27d11963a5078136cedc696156. --- core/frontend/services/url/UrlGenerator.js | 19 +----- core/frontend/services/url/configs/canary.js | 4 +- core/frontend/services/url/configs/v0.1.js | 4 +- core/frontend/services/url/configs/v2.js | 4 +- .../canary/utils/serializers/input/pages.js | 25 ++----- .../canary/utils/serializers/input/posts.js | 25 ++----- .../utils/serializers/output/utils/mapper.js | 6 -- core/server/api/v0.1/posts.js | 61 ++--------------- .../api/v2/utils/serializers/input/pages.js | 25 ++----- .../api/v2/utils/serializers/input/posts.js | 25 ++----- .../utils/serializers/output/utils/mapper.js | 6 -- .../data/importer/importers/data/posts.js | 18 +++-- .../server/data/schema/fixtures/fixtures.json | 14 ++-- core/server/data/validation/index.js | 3 +- core/server/models/base/index.js | 6 +- core/server/models/plugins/filter.js | 4 +- core/server/models/plugins/include-count.js | 4 +- core/server/models/post.js | 18 ++--- core/server/services/slack.js | 2 +- core/server/services/xmlrpc.js | 2 +- core/shared/nql-map-key-values/README.md | 26 -------- core/shared/nql-map-key-values/index.js | 44 ------------- core/shared/nql-map-key-values/package.json | 14 ---- core/test/acceptance/old/admin/pages_spec.js | 4 +- core/test/acceptance/old/admin/utils.js | 4 -- core/test/acceptance/old/content/utils.js | 2 - .../api/canary/admin/schedules_spec.js | 2 +- .../regression/api/canary/content/utils.js | 1 - core/test/regression/api/v0.1/utils.js | 2 - .../regression/api/v2/admin/schedules_spec.js | 2 +- core/test/regression/api/v2/content/utils.js | 1 - .../regression/models/model_posts_spec.js | 18 ++--- core/test/regression/site/site_spec.js | 4 +- .../utils/serializers/input/pages_spec.js | 18 ++--- .../utils/serializers/input/posts_spec.js | 30 ++++----- .../v2/utils/serializers/input/pages_spec.js | 18 ++--- .../v2/utils/serializers/input/posts_spec.js | 30 ++++----- .../importer/importers/data/posts_spec.js | 66 ++++--------------- core/test/unit/data/schema/integrity_spec.js | 4 +- core/test/unit/data/validation/index_spec.js | 8 ++- core/test/unit/models/post_spec.js | 56 ++++++++-------- .../unit/services/rss/generate-feed_spec.js | 2 +- core/test/unit/services/slack_spec.js | 2 +- core/test/utils/fixtures/data-generator.js | 6 +- 44 files changed, 176 insertions(+), 463 deletions(-) delete mode 100644 core/shared/nql-map-key-values/README.md delete mode 100644 core/shared/nql-map-key-values/index.js delete mode 100644 core/shared/nql-map-key-values/package.json diff --git a/core/frontend/services/url/UrlGenerator.js b/core/frontend/services/url/UrlGenerator.js index 3b44966bdf..46b53a8df7 100644 --- a/core/frontend/services/url/UrlGenerator.js +++ b/core/frontend/services/url/UrlGenerator.js @@ -23,8 +23,6 @@ const _ = require('lodash'), replacement: 'primary_author.slug' }]; -const mapNQLKeyValues = require('../../../shared/nql-map-key-values'); - /** * The UrlGenerator class is responsible to generate urls based on a router's conditions. * It is the component which sits between routers and resources and connects them together. @@ -46,22 +44,7 @@ class UrlGenerator { // CASE: routers can define custom filters, but not required. if (this.router.getFilter()) { this.filter = this.router.getFilter(); - this.nql = nql(this.filter, { - expansions: EXPANSIONS, - transformer: mapNQLKeyValues({ - key: { - from: 'page', - to: 'type' - }, - values: [{ - from: false, - to: 'post' - }, { - from: true, - to: 'page' - }] - }) - }); + this.nql = nql(this.filter, {expansions: EXPANSIONS}); debug('filter', this.filter); } diff --git a/core/frontend/services/url/configs/canary.js b/core/frontend/services/url/configs/canary.js index 0f4d3458be..6d91c3a483 100644 --- a/core/frontend/services/url/configs/canary.js +++ b/core/frontend/services/url/configs/canary.js @@ -8,7 +8,7 @@ module.exports = [ type: 'posts', modelOptions: { modelName: 'Post', - filter: 'visibility:public+status:published+type:post', + filter: 'visibility:public+status:published+page:false', exclude: [ 'title', 'mobiledoc', @@ -79,7 +79,7 @@ module.exports = [ 'primary_tag', 'primary_author' ], - filter: 'visibility:public+status:published+type:page' + filter: 'visibility:public+status:published+page:true' }, events: { add: 'page.published', diff --git a/core/frontend/services/url/configs/v0.1.js b/core/frontend/services/url/configs/v0.1.js index ed6ccc42cc..55fa266a76 100644 --- a/core/frontend/services/url/configs/v0.1.js +++ b/core/frontend/services/url/configs/v0.1.js @@ -8,7 +8,7 @@ module.exports = [ type: 'posts', modelOptions: { modelName: 'Post', - filter: 'visibility:public+status:published+type:post', + filter: 'visibility:public+status:published+page:false', exclude: [ 'title', 'mobiledoc', @@ -73,7 +73,7 @@ module.exports = [ 'primary_tag', 'primary_author' ], - filter: 'visibility:public+status:published+type:page' + filter: 'visibility:public+status:published+page:true' }, events: { add: 'page.published', diff --git a/core/frontend/services/url/configs/v2.js b/core/frontend/services/url/configs/v2.js index 0f4d3458be..6d91c3a483 100644 --- a/core/frontend/services/url/configs/v2.js +++ b/core/frontend/services/url/configs/v2.js @@ -8,7 +8,7 @@ module.exports = [ type: 'posts', modelOptions: { modelName: 'Post', - filter: 'visibility:public+status:published+type:post', + filter: 'visibility:public+status:published+page:false', exclude: [ 'title', 'mobiledoc', @@ -79,7 +79,7 @@ module.exports = [ 'primary_tag', 'primary_author' ], - filter: 'visibility:public+status:published+type:page' + filter: 'visibility:public+status:published+page:true' }, events: { add: 'page.published', diff --git a/core/server/api/canary/utils/serializers/input/pages.js b/core/server/api/canary/utils/serializers/input/pages.js index 42446cd87d..696d3e0744 100644 --- a/core/server/api/canary/utils/serializers/input/pages.js +++ b/core/server/api/canary/utils/serializers/input/pages.js @@ -1,24 +1,9 @@ const _ = require('lodash'); const debug = require('ghost-ignition').debug('api:canary:utils:serializers:input:pages'); -const mapNQLKeyValues = require('../../../../../../shared/nql-map-key-values'); const converters = require('../../../../../lib/mobiledoc/converters'); const url = require('./utils/url'); const localUtils = require('../../index'); -const replacePageWithType = mapNQLKeyValues({ - key: { - from: 'page', - to: 'type' - }, - values: [{ - from: false, - to: 'post' - }, { - from: true, - to: 'page' - }] -}); - function removeMobiledocFormat(frame) { if (frame.options.formats && frame.options.formats.includes('mobiledoc')) { frame.options.formats = frame.options.formats.filter((format) => { @@ -70,9 +55,9 @@ function defaultFormat(frame) { */ const forcePageFilter = (frame) => { if (frame.options.filter) { - frame.options.filter = `(${frame.options.filter})+type:page`; + frame.options.filter = `(${frame.options.filter})+page:true`; } else { - frame.options.filter = 'type:page'; + frame.options.filter = 'page:true'; } }; @@ -101,8 +86,6 @@ module.exports = { defaultRelations(frame); } - frame.options.mongoTransformer = replacePageWithType; - debug(frame.options); }, @@ -140,7 +123,7 @@ module.exports = { // @NOTE: force storing page if (options.add) { - frame.data.pages[0].type = 'page'; + frame.data.pages[0].page = true; } // CASE: Transform short to long format @@ -180,7 +163,7 @@ module.exports = { destroy(apiConfig, frame) { frame.options.destroyBy = { id: frame.options.id, - type: 'page' + page: true }; defaultFormat(frame); diff --git a/core/server/api/canary/utils/serializers/input/posts.js b/core/server/api/canary/utils/serializers/input/posts.js index ba484d59b2..e61b212685 100644 --- a/core/server/api/canary/utils/serializers/input/posts.js +++ b/core/server/api/canary/utils/serializers/input/posts.js @@ -1,25 +1,10 @@ const _ = require('lodash'); const debug = require('ghost-ignition').debug('api:canary:utils:serializers:input:posts'); -const mapNQLKeyValues = require('../../../../../../shared/nql-map-key-values'); const url = require('./utils/url'); const localUtils = require('../../index'); const labs = require('../../../../../services/labs'); const converters = require('../../../../../lib/mobiledoc/converters'); -const replacePageWithType = mapNQLKeyValues({ - key: { - from: 'page', - to: 'type' - }, - values: [{ - from: false, - to: 'post' - }, { - from: true, - to: 'page' - }] -}); - function removeMobiledocFormat(frame) { if (frame.options.formats && frame.options.formats.includes('mobiledoc')) { frame.options.formats = frame.options.formats.filter((format) => { @@ -79,9 +64,9 @@ function defaultFormat(frame) { */ const forcePageFilter = (frame) => { if (frame.options.filter) { - frame.options.filter = `(${frame.options.filter})+type:post`; + frame.options.filter = `(${frame.options.filter})+page:false`; } else { - frame.options.filter = 'type:post'; + frame.options.filter = 'page:false'; } }; @@ -123,8 +108,6 @@ module.exports = { defaultRelations(frame); } - frame.options.mongoTransformer = replacePageWithType; - debug(frame.options); }, @@ -175,7 +158,7 @@ module.exports = { // @NOTE: force adding post if (options.add) { - frame.data.posts[0].type = 'post'; + frame.data.posts[0].page = false; } // CASE: Transform short to long format @@ -213,7 +196,7 @@ module.exports = { destroy(apiConfig, frame) { frame.options.destroyBy = { id: frame.options.id, - type: 'post' + page: false }; defaultFormat(frame); diff --git a/core/server/api/canary/utils/serializers/output/utils/mapper.js b/core/server/api/canary/utils/serializers/output/utils/mapper.js index 3958d2c364..77aab97d44 100644 --- a/core/server/api/canary/utils/serializers/output/utils/mapper.js +++ b/core/server/api/canary/utils/serializers/output/utils/mapper.js @@ -35,10 +35,6 @@ const mapPost = (model, frame) => { url.forPost(model.id, jsonModel, frame); if (utils.isContentAPI(frame)) { - // Content api v2 still expects page prop - if (jsonModel.type === 'page') { - jsonModel.page = true; - } date.forPost(jsonModel); members.forPost(jsonModel, frame); } @@ -61,8 +57,6 @@ const mapPost = (model, frame) => { }); } - delete jsonModel.type; - return jsonModel; }; diff --git a/core/server/api/v0.1/posts.js b/core/server/api/v0.1/posts.js index 6e55ab7c48..e7ba18f41c 100644 --- a/core/server/api/v0.1/posts.js +++ b/core/server/api/v0.1/posts.js @@ -16,49 +16,6 @@ const Promise = require('bluebird'), ], unsafeAttrs = ['author_id', 'status', 'authors']; -const mapNQLKeyValues = require('../../../shared/nql-map-key-values'); - -const replacePageWithType = mapNQLKeyValues({ - key: { - from: 'page', - to: 'type' - }, - values: [{ - from: false, - to: 'post' - }, { - from: true, - to: 'page' - }] -}); - -function convertTypeToPage(model) { - // Respect include param - if (!Object.hasOwnProperty.call(model, 'type')) { - return model; - } - model.page = model.type === 'page'; - delete model.type; - return model; -} - -function convertPageToType(model) { - if (!Object.hasOwnProperty.call(model, 'page')) { - return model; - } - - if (model.page === true) { - model.type = 'page'; - } else if (model.page === false) { - model.type = 'post'; - } else { - // This is to ensure that invalid page props generate a ValidationError - model.type = 'UNKNOWN_PAGE_OPTION'; - } - delete model.page; - return model; -} - let posts; /** @@ -101,12 +58,10 @@ posts = { * @returns {Object} options */ function modelQuery(options) { - options.mongoTransformer = replacePageWithType; - return models.Post.findPage(options) .then(({data, meta}) => { return { - posts: data.map(model => urlsForPost(model.id, model.toJSON(options), options)).map(convertTypeToPage), + posts: data.map(model => urlsForPost(model.id, model.toJSON(options), options)), meta: meta }; }); @@ -146,8 +101,6 @@ posts = { * @returns {Object} options */ function modelQuery(options) { - options.mongoTransformer = replacePageWithType; - return models.Post.findOne(options.data, omit(options, ['data'])) .then((model) => { if (!model) { @@ -157,7 +110,7 @@ posts = { } return { - posts: [urlsForPost(model.id, model.toJSON(options), options)].map(convertTypeToPage) + posts: [urlsForPost(model.id, model.toJSON(options), options)] }; }); } @@ -195,7 +148,7 @@ posts = { * @returns {Object} options */ function modelQuery(options) { - return models.Post.edit(options.data.posts.map(convertPageToType)[0], omit(options, ['data'])) + return models.Post.edit(options.data.posts[0], omit(options, ['data'])) .then((model) => { if (!model) { return Promise.reject(new common.errors.NotFoundError({ @@ -213,7 +166,7 @@ posts = { } return { - posts: [post].map(convertTypeToPage) + posts: [post] }; }); } @@ -249,7 +202,7 @@ posts = { * @returns {Object} options */ function modelQuery(options) { - return models.Post.add(options.data.posts.map(convertPageToType)[0], omit(options, ['data'])) + return models.Post.add(options.data.posts[0], omit(options, ['data'])) .then((model) => { const post = urlsForPost(model.id, model.toJSON(options), options); @@ -258,9 +211,7 @@ posts = { post.statusChanged = true; } - return { - posts: [post].map(convertTypeToPage) - }; + return {posts: [post]}; }); } diff --git a/core/server/api/v2/utils/serializers/input/pages.js b/core/server/api/v2/utils/serializers/input/pages.js index 2f30e3cb71..b8587bb9f4 100644 --- a/core/server/api/v2/utils/serializers/input/pages.js +++ b/core/server/api/v2/utils/serializers/input/pages.js @@ -1,24 +1,9 @@ const _ = require('lodash'); -const mapNQLKeyValues = require('../../../../../../shared/nql-map-key-values'); const debug = require('ghost-ignition').debug('api:v2:utils:serializers:input:pages'); const converters = require('../../../../../lib/mobiledoc/converters'); const url = require('./utils/url'); const localUtils = require('../../index'); -const replacePageWithType = mapNQLKeyValues({ - key: { - from: 'page', - to: 'type' - }, - values: [{ - from: false, - to: 'post' - }, { - from: true, - to: 'page' - }] -}); - function removeMobiledocFormat(frame) { if (frame.options.formats && frame.options.formats.includes('mobiledoc')) { frame.options.formats = frame.options.formats.filter((format) => { @@ -70,9 +55,9 @@ function defaultFormat(frame) { */ const forcePageFilter = (frame) => { if (frame.options.filter) { - frame.options.filter = `(${frame.options.filter})+type:page`; + frame.options.filter = `(${frame.options.filter})+page:true`; } else { - frame.options.filter = 'type:page'; + frame.options.filter = 'page:true'; } }; @@ -101,8 +86,6 @@ module.exports = { defaultRelations(frame); } - frame.options.mongoTransformer = replacePageWithType; - debug(frame.options); }, @@ -140,7 +123,7 @@ module.exports = { // @NOTE: force storing page if (options.add) { - frame.data.pages[0].type = 'page'; + frame.data.pages[0].page = true; } // CASE: Transform short to long format @@ -180,7 +163,7 @@ module.exports = { destroy(apiConfig, frame) { frame.options.destroyBy = { id: frame.options.id, - type: 'page' + page: true }; defaultFormat(frame); diff --git a/core/server/api/v2/utils/serializers/input/posts.js b/core/server/api/v2/utils/serializers/input/posts.js index ac82eecec5..2a5271d688 100644 --- a/core/server/api/v2/utils/serializers/input/posts.js +++ b/core/server/api/v2/utils/serializers/input/posts.js @@ -1,25 +1,10 @@ const _ = require('lodash'); -const mapNQLKeyValues = require('../../../../../../shared/nql-map-key-values'); const debug = require('ghost-ignition').debug('api:v2:utils:serializers:input:posts'); const url = require('./utils/url'); const localUtils = require('../../index'); const labs = require('../../../../../services/labs'); const converters = require('../../../../../lib/mobiledoc/converters'); -const replacePageWithType = mapNQLKeyValues({ - key: { - from: 'page', - to: 'type' - }, - values: [{ - from: false, - to: 'post' - }, { - from: true, - to: 'page' - }] -}); - function removeMobiledocFormat(frame) { if (frame.options.formats && frame.options.formats.includes('mobiledoc')) { frame.options.formats = frame.options.formats.filter((format) => { @@ -79,9 +64,9 @@ function defaultFormat(frame) { */ const forcePageFilter = (frame) => { if (frame.options.filter) { - frame.options.filter = `(${frame.options.filter})+type:post`; + frame.options.filter = `(${frame.options.filter})+page:false`; } else { - frame.options.filter = 'type:post'; + frame.options.filter = 'page:false'; } }; @@ -123,8 +108,6 @@ module.exports = { defaultRelations(frame); } - frame.options.mongoTransformer = replacePageWithType; - debug(frame.options); }, @@ -175,7 +158,7 @@ module.exports = { // @NOTE: force adding post if (options.add) { - frame.data.posts[0].type = 'post'; + frame.data.posts[0].page = false; } // CASE: Transform short to long format @@ -213,7 +196,7 @@ module.exports = { destroy(apiConfig, frame) { frame.options.destroyBy = { id: frame.options.id, - type: 'post' + page: false }; defaultFormat(frame); diff --git a/core/server/api/v2/utils/serializers/output/utils/mapper.js b/core/server/api/v2/utils/serializers/output/utils/mapper.js index 3958d2c364..77aab97d44 100644 --- a/core/server/api/v2/utils/serializers/output/utils/mapper.js +++ b/core/server/api/v2/utils/serializers/output/utils/mapper.js @@ -35,10 +35,6 @@ const mapPost = (model, frame) => { url.forPost(model.id, jsonModel, frame); if (utils.isContentAPI(frame)) { - // Content api v2 still expects page prop - if (jsonModel.type === 'page') { - jsonModel.page = true; - } date.forPost(jsonModel); members.forPost(jsonModel, frame); } @@ -61,8 +57,6 @@ const mapPost = (model, frame) => { }); } - delete jsonModel.type; - return jsonModel; }; diff --git a/core/server/data/importer/importers/data/posts.js b/core/server/data/importer/importers/data/posts.js index 3bdf0c1a46..db98b3b9a0 100644 --- a/core/server/data/importer/importers/data/posts.js +++ b/core/server/data/importer/importers/data/posts.js @@ -21,16 +21,6 @@ class PostsImporter extends BaseImporter { if (!validation.validator.isUUID(obj.uuid || '')) { obj.uuid = uuid.v4(); } - - // we used to have post.page=true/false - // we now have post.type='page'/'post' - // give precedence to post.type if both are present - if (_.has(obj, 'page')) { - if (_.isEmpty(obj.type)) { - obj.type = obj.page ? 'page' : 'post'; - } - delete obj.page; - } }); } @@ -162,6 +152,14 @@ class PostsImporter extends BaseImporter { this.addNestedRelations(); _.each(this.dataToImport, (model) => { + // during 2.28.x we had `post.type` in place of `post.page` + // this needs normalising back to `post.page` + // TODO: switch back to `post.page->type` in v3 + if (_.has(model, 'type')) { + model.page = model.type === 'post' ? false : true; + delete model.type; + } + // NOTE: we remember the original post id for disqus // (see https://github.com/TryGhost/Ghost/issues/8963) diff --git a/core/server/data/schema/fixtures/fixtures.json b/core/server/data/schema/fixtures/fixtures.json index e3f327e6c9..1f9cc14799 100644 --- a/core/server/data/schema/fixtures/fixtures.json +++ b/core/server/data/schema/fixtures/fixtures.json @@ -475,7 +475,7 @@ "slug": "themes", "mobiledoc": "{\"version\":\"0.3.1\",\"atoms\":[],\"cards\":[[\"image\",{\"src\":\"https://static.ghost.org/v1.0.0/images/marketplace.jpg\",\"caption\":\"Anyone can write a completely custom Ghost theme with some solid knowledge of HTML and CSS\"}]],\"markups\":[[\"a\",[\"href\",\"https://ghost.org/marketplace/\"]],[\"code\"],[\"a\",[\"href\",\"https://github.com/TryGhost/Casper\"]],[\"a\",[\"href\",\"https://ghost.org/docs/api/handlebars-themes/\"]],[\"strong\"],[\"a\",[\"href\",\"https://forum.ghost.org/c/themes\"]]],\"sections\":[[1,\"p\",[[0,[],0,\"Ghost comes with a beautiful default theme called Casper, which is designed to be a clean, readable publication layout and can be adapted for most purposes. However, Ghost can also be completely themed to suit your needs. Rather than just giving you a few basic settings which act as a poor proxy for code, we just let you write code.\"]]],[1,\"p\",[[0,[],0,\"There are a huge range of both free and premium pre-built themes which you can get from the \"],[0,[0],1,\"Ghost Theme Marketplace\"],[0,[],0,\", or you can create your own from scratch.\"]]],[10,0],[1,\"p\",[[0,[],0,\"Ghost themes are written with a templating language called handlebars, which has a set of dynamic helpers to insert your data into template files. For example: \"],[0,[1],1,\"{{author.name}}\"],[0,[],0,\" outputs the name of the current author.\"]]],[1,\"p\",[[0,[],0,\"The best way to learn how to write your own Ghost theme is to have a look at \"],[0,[2],1,\"the source code for Casper\"],[0,[],0,\", which is heavily commented and should give you a sense of how everything fits together.\"]]],[3,\"ul\",[[[0,[1],1,\"default.hbs\"],[0,[],0,\" is the main template file, all contexts will load inside this file unless specifically told to use a different template.\"]],[[0,[1],1,\"post.hbs\"],[0,[],0,\" is the file used in the context of viewing a post.\"]],[[0,[1],1,\"index.hbs\"],[0,[],0,\" is the file used in the context of viewing the home page.\"]],[[0,[],0,\"and so on\"]]]],[1,\"p\",[[0,[],0,\"We've got \"],[0,[3],1,\"full and extensive theme documentation\"],[0,[],0,\" which outlines every template file, context and helper that you can use.\"]]],[1,\"p\",[[0,[],0,\"If you want to chat with other people making Ghost themes to get any advice or help, there's also a \"],[0,[4],1,\"themes\"],[0,[],0,\" section on our \"],[0,[5],1,\"public Ghost forum\"],[0,[],0,\".\"]]]]}", "featured": false, - "type": "post", + "page": false, "status": "published", "meta_title": null, "meta_description": null, @@ -490,7 +490,7 @@ "slug": "apps-integrations", "mobiledoc": "{\"version\":\"0.3.1\",\"atoms\":[],\"cards\":[[\"markdown\",{\"markdown\":\"\\n\"}]],\"markups\":[[\"a\",[\"href\",\"https://zapier.com\"]],[\"strong\"],[\"a\",[\"href\",\"https://ghost.org/docs/api/handlebars-themes/\"]],[\"em\"],[\"a\",[\"href\",\"https://ghost.org/integrations/disqus/\"]],[\"a\",[\"href\",\"https://ghost.org/integrations/discourse/\"]],[\"a\",[\"href\",\"https://ghost.org/integrations/\"]],[\"a\",[\"href\",\"https://prismjs.com/\"]],[\"a\",[\"href\",\"https://www.google.com/forms/\"]],[\"a\",[\"href\",\"https://www.typeform.com/\"]],[\"a\",[\"href\",\"https://ghost.org/docs/api/\"]],[\"a\",[\"href\",\"/themes/\"]]],\"sections\":[[1,\"p\",[[0,[],0,\"There are three primary ways to work with third-party services in Ghost: using Zapier, editing your theme, or using the Ghost API.\"]]],[1,\"h1\",[[0,[],0,\"Zapier\"]]],[1,\"p\",[[0,[],0,\"You can connect your Ghost site to over 1,000 external services using the official integration with \"],[0,[0],1,\"Zapier\"],[0,[],0,\".\"]]],[1,\"p\",[[0,[],0,\"Zapier sets up automations with Triggers and Actions, which allows you to create and customise a wide range of connected applications.\"]]],[1,\"blockquote\",[[0,[1],1,\"Example\"],[0,[],0,\": When someone new subscribes to a newsletter on a Ghost site (Trigger) then the contact information is automatically pushed into MailChimp (Action).\"]]],[1,\"p\",[[0,[1],1,\"Here are the most popular Ghost<>Zapier automation templates:\"],[0,[],0,\" \"]]],[10,0],[1,\"h1\",[[0,[],0,\"Editing your theme\"]]],[1,\"p\",[[0,[],0,\"One of the biggest advantages of using Ghost over centralised platforms is that you have total control over the front end of your site. Either customise your existing theme, or create a new theme from scratch with our \"],[0,[2],1,\"Theme SDK\"],[0,[],0,\". \"]]],[1,\"p\",[[0,[],0,\"You can integrate \"],[0,[3],1,\"any\"],[0,[],0,\" front end code into a Ghost theme without restriction, and it will work just fine. No restrictions!\"]]],[1,\"p\",[[0,[1],1,\"Here are some common examples\"],[0,[],0,\":\"]]],[3,\"ul\",[[[0,[],0,\"Include comments on a Ghost blog with \"],[0,[4],1,\"Disqus\"],[0,[],0,\" or \"],[0,[5],1,\"Discourse\"]],[[0,[],0,\"Implement \"],[0,[6],1,\"MathJAX\"],[0,[],0,\" with a little bit of JavaScript\"]],[[0,[],0,\"Add syntax highlighting to your code snippets using \"],[0,[7],1,\"Prism.js\"]],[[0,[],0,\"Integrate any dynamic forms from \"],[0,[8],1,\"Google\"],[0,[],0,\" or \"],[0,[9],1,\"Typeform\"],[0,[],0,\" to capture data\"]],[[0,[],0,\"Just about anything which uses JavaScript, APIs and Markup.\"]]]],[1,\"h1\",[[0,[],0,\"Using the Public API\"]]],[1,\"p\",[[0,[],0,\"Ghost itself is driven by a set of core APIs, and so you can access the Public Ghost JSON API from external webpages or applications in order to pull data and display it in other places.\"]]],[1,\"blockquote\",[[0,[],0,\"The Ghost API is \"],[0,[10],1,\"thoroughly documented\"],[0,[],0,\" and straightforward to work with for developers of almost any level. \"]]],[1,\"p\",[[0,[],0,\"Alright, the last post in our welcome-series! If you're curious about creating your own Ghost theme from scratch, here are \"],[0,[11],1,\"some more details\"],[0,[],0,\" on how that works.\"]]]]}", "featured": false, - "type": "post", + "page": false, "status": "published", "meta_title": null, "meta_description": null, @@ -505,7 +505,7 @@ "slug": "organising-content", "mobiledoc": "{\"version\":\"0.3.1\",\"atoms\":[[\"soft-return\",\"\",{}]],\"cards\":[],\"markups\":[[\"strong\"],[\"code\"],[\"em\"],[\"a\",[\"href\",\"https://ghost.org/docs/api/handlebars-themes/\"]],[\"a\",[\"href\",\"http://yaml.org/spec/1.2/spec.html\",\"rel\",\"noreferrer nofollow noopener\"]],[\"a\",[\"href\",\"https://ghost.org/docs/api/handlebars-themes/routing/\"]],[\"a\",[\"href\",\"/apps-integrations/\"]]],\"sections\":[[1,\"p\",[[0,[],0,\"Ghost has a flexible organisational taxonomy called\"],[0,[0],1,\" tags\"],[0,[],0,\" which can be used to configure your site structure using \"],[0,[0],1,\"dynamic routing\"],[0,[],0,\". \"]]],[1,\"h1\",[[0,[],0,\"Basic Tagging\"]]],[1,\"p\",[[0,[],0,\"You can think of tags like Gmail labels. By tagging posts with one or more keyword, you can organise articles into buckets of related content.\"]]],[1,\"p\",[[0,[],0,\"When you create content for your publication you can assign tags to help differentiate between categories of content. \"]]],[1,\"p\",[[0,[],0,\"For example you may tag some content with News and other content with Podcast, which would create two distinct categories of content listed on \"],[0,[1],1,\"/tag/news/\"],[0,[],0,\" and \"],[0,[1],1,\"/tag/weather/\"],[0,[],0,\", respectively.\"]]],[1,\"p\",[[0,[],0,\"If you tag a post with both \"],[0,[1],1,\"News\"],[0,[],0,\" \"],[0,[2],1,\"and\"],[0,[],0,\" \"],[0,[1],1,\"Weather\"],[0,[],0,\" - then it appears in both sections. Tag archives are like dedicated home-pages for each category of content that you have. They have their own pages, their own RSS feeds, and can support their own cover images and meta data.\"]]],[1,\"h1\",[[0,[],0,\"The primary tag\"]]],[1,\"p\",[[0,[],0,\"Inside the Ghost editor, you can drag and drop tags into a specific order. The first tag in the list is always given the most importance, and some themes will only display the primary tag (the first tag in the list) by default. \"]]],[1,\"blockquote\",[[0,[2,0],1,\"News\"],[0,[],1,\", Technology, Startup\"]]],[1,\"p\",[[0,[],0,\"So you can add the most important tag which you want to show up in your theme, but also add related tags which are less important.\"]]],[1,\"h1\",[[0,[],0,\"Private tags\"]]],[1,\"p\",[[0,[],0,\"Sometimes you may want to assign a post a specific tag, but you don't necessarily want that tag appearing in the theme or creating an archive page. In Ghost, hashtags are private and can be used for special styling.\"]]],[1,\"p\",[[0,[],0,\"For example, if you sometimes publish posts with video content - you might want your theme to adapt and get rid of the sidebar for these posts, to give more space for an embedded video to fill the screen. In this case, you could use private tags to tell your theme what to do.\"]]],[1,\"blockquote\",[[0,[2,0],1,\"News\"],[0,[],1,\", #video\"]]],[1,\"p\",[[0,[],0,\"Here, the theme would assign the post publicly displayed tags of News - but it would also keep a private record of the post being tagged with #video. In your theme, you could then look for private tags conditionally and give them special formatting. \"]]],[1,\"blockquote\",[[0,[2],0,\"You can find documentation for theme development techniques like this and many more over on Ghost's extensive \"],[0,[3],1,\"theme documentation\"],[0,[],1,\".\"]]],[1,\"h1\",[[0,[],0,\"Dynamic Routing\"]]],[1,\"p\",[[0,[],0,\"Dynamic routing gives you the ultimate freedom to build a custom publication to suit your needs. Routes are rules that map URL patterns to your content and templates. \"]]],[1,\"p\",[[0,[],0,\"For example, you may not want content tagged with \"],[0,[1],1,\"News\"],[0,[],0,\" to exist on: \"],[0,[1],1,\"example.com/tag/news\"],[0,[],0,\". Instead, you want it to exist on \"],[0,[1],1,\"example.com/news\"],[0,[],0,\" . \"]]],[1,\"p\",[[0,[],0,\"In this case you can use dynamic routes to create customised collections of content on your site. It's also possible to use multiple templates in your theme to render each content type differently.\"]]],[1,\"p\",[[0,[],0,\"There are lots of use cases for dynamic routing with Ghost, here are a few common examples: \"]]],[3,\"ul\",[[[0,[],0,\"Setting a custom home page with its own template\"]],[[0,[],0,\"Having separate content hubs for blog and podcast, that render differently, and have custom RSS feeds to support two types of content\"]],[[0,[],0,\"Creating a founders column as a unique view, by filtering content created by specific authors\"]],[[0,[],0,\"Including dates in permalinks for your posts\"]],[[0,[],0,\"Setting posts to have a URL relative to their primary tag like \"],[0,[1],1,\"example.com/europe/story-title/\"],[1,[],0,0]]]],[1,\"blockquote\",[[0,[2],0,\"Dynamic routing can be configured in Ghost using \"],[0,[4],1,\"YAML\"],[0,[],0,\" files. Read our dynamic routing \"],[0,[5],1,\"documentation\"],[0,[],1,\" for further details.\"]]],[1,\"p\",[[0,[],0,\"You can further customise your site using \"],[0,[6],1,\"Apps & Integrations\"],[0,[],0,\".\"]]]]}", "featured": false, - "type": "post", + "page": false, "status": "published", "meta_title": null, "meta_description": null, @@ -520,7 +520,7 @@ "slug": "admin-settings", "mobiledoc": "{\"version\":\"0.3.1\",\"atoms\":[[\"soft-return\",\"\",{}],[\"soft-return\",\"\",{}],[\"soft-return\",\"\",{}],[\"soft-return\",\"\",{}],[\"soft-return\",\"\",{}],[\"soft-return\",\"\",{}],[\"soft-return\",\"\",{}]],\"cards\":[[\"image\",{\"src\":\"https://static.ghost.org/v1.0.0/images/private.png\"}],[\"hr\",{}]],\"markups\":[[\"a\",[\"href\",\"/ghost/settings/general/\"]],[\"em\"],[\"strong\"],[\"a\",[\"href\",\"https://ghost.org/pricing/\"]],[\"a\",[\"href\",\"/organising-content/\"]]],\"sections\":[[1,\"p\",[[0,[],0,\"There are a couple of things to do next while you're getting set up:\"]]],[1,\"h1\",[[0,[],0,\"Make your site private\"]]],[1,\"p\",[[0,[],0,\"If you've got a publication that you don't want the world to see yet because it's not ready to launch, you can hide your Ghost site behind a basic shared pass-phrase.\"]]],[1,\"p\",[[0,[],0,\"You can toggle this preference on at the bottom of Ghost's \"],[0,[0],1,\"General Settings\"],[0,[],0,\":\"]]],[10,0],[1,\"p\",[[0,[],0,\"Ghost will give you a short, randomly generated pass-phrase which you can share with anyone who needs access to the site while you're working on it. While this setting is enabled, all search engine optimisation features will be switched off to help keep your site under the radar.\"]]],[1,\"p\",[[0,[],0,\"Do remember though, this is \"],[0,[1],1,\"not\"],[0,[],0,\" secure authentication. You shouldn't rely on this feature for protecting important private data. It's just a simple, shared pass-phrase for some very basic privacy.\"]]],[10,1],[1,\"h1\",[[0,[],0,\"Invite your team \"]]],[1,\"p\",[[0,[],0,\"Ghost has a number of different user roles for your team:\"]]],[1,\"p\",[[0,[2],1,\"Contributors\"],[1,[],0,0],[0,[],0,\"This is the base user level in Ghost. Contributors can create and edit their own draft posts, but they are unable to edit drafts of others or publish posts. Contributors are \"],[0,[2],1,\"untrusted\"],[0,[],0,\" users with the most basic access to your publication.\"]]],[1,\"p\",[[0,[2],1,\"Authors\"],[1,[],0,1],[0,[],0,\"Authors are the 2nd user level in Ghost. Authors can write, edit and publish their own posts. Authors are \"],[0,[2],1,\"trusted\"],[0,[],0,\" users. If you don't trust users to be allowed to publish their own posts, they should be set as Contributors.\"]]],[1,\"p\",[[0,[2],1,\"Editors\"],[1,[],0,2],[0,[],0,\"Editors are the 3rd user level in Ghost. Editors can do everything that an Author can do, but they can also edit and publish the posts of others - as well as their own. Editors can also invite new Contributors+Authors to the site.\"]]],[1,\"p\",[[0,[2],1,\"Administrators\"],[1,[],0,3],[0,[],0,\"The top user level in Ghost is Administrator. Again, administrators can do everything that Authors and Editors can do, but they can also edit all site settings and data, not just content. Additionally, administrators have full access to invite, manage or remove any other user of the site.\"],[1,[],0,4],[1,[],0,5],[0,[2],1,\"The Owner\"],[1,[],0,6],[0,[],0,\"There is only ever one owner of a Ghost site. The owner is a special user which has all the same permissions as an Administrator, but with two exceptions: The Owner can never be deleted. And in some circumstances the owner will have access to additional special settings if applicable. For example: billing details, if using \"],[0,[3,2],2,\"Ghost(Pro)\"],[0,[],0,\".\"]]],[1,\"blockquote\",[[0,[1],1,\"It's a good idea to ask all of your users to fill out their user profiles, including bio and social links. These will populate rich structured data for posts and generally create more opportunities for themes to fully populate their design.\"]]],[1,\"p\",[[0,[],0,\"Next up: \"],[0,[4],1,\"Organising your content\"],[0,[],0,\" \"]]]]}", "featured": false, - "type": "post", + "page": false, "status": "published", "meta_title": null, "meta_description": null, @@ -535,7 +535,7 @@ "slug": "publishing-options", "mobiledoc": "{\"version\":\"0.3.1\",\"atoms\":[],\"cards\":[[\"code\",{\"code\":\"{\\n \\\"@context\\\": \\\"https://schema.org\\\",\\n \\\"@type\\\": \\\"Article\\\",\\n \\\"publisher\\\": {\\n \\\"@type\\\": \\\"Organization\\\",\\n \\\"name\\\": \\\"Publishing options\\\",\\n \\\"logo\\\": \\\"https://static.ghost.org/ghost-logo.svg\\\"\\n },\\n \\\"author\\\": {\\n \\\"@type\\\": \\\"Person\\\",\\n \\\"name\\\": \\\"Ghost\\\",\\n \\\"url\\\": \\\"http://demo.ghost.io/author/ghost/\\\",\\n \\\"sameAs\\\": []\\n },\\n \\\"headline\\\": \\\"Publishing options\\\",\\n \\\"url\\\": \\\"http://demo.ghost.io/publishing-options\\\",\\n \\\"datePublished\\\": \\\"2018-08-08T11:44:00.000Z\\\",\\n \\\"dateModified\\\": \\\"2018-08-09T12:06:21.000Z\\\",\\n \\\"keywords\\\": \\\"Getting Started\\\",\\n \\\"description\\\": \\\"The Ghost editor has everything you need to fully optimise your content. This is where you can add tags and authors, feature a post, or turn a post into a page.\\\",\\n }\\n}\\n \"}]],\"markups\":[[\"a\",[\"href\",\"https://schema.org/\"]],[\"a\",[\"href\",\"https://search.google.com/structured-data/testing-tool\",\"rel\",\"noreferrer nofollow noopener\"]],[\"strong\"],[\"a\",[\"href\",\"/ghost/settings/code-injection/\"]],[\"a\",[\"href\",\"/admin-settings/\"]]],\"sections\":[[1,\"p\",[[0,[],0,\"The Ghost editor has everything you need to fully optimise your content. This is where you can add tags and authors, feature a post, or turn a post into a page. \"]]],[1,\"blockquote\",[[0,[],0,\"Access the post settings menu in the top right hand corner of the editor. \"]]],[1,\"h2\",[[0,[],0,\"Post feature image\"]]],[1,\"p\",[[0,[],0,\"Insert your post feature image from the very top of the post settings menu. Consider resizing or optimising your image first to ensure it's an appropriate size.\"]]],[1,\"h2\",[[0,[],0,\"Structured data & SEO\"]]],[1,\"p\",[[0,[],0,\"Customise your social media sharing cards for Facebook and Twitter, enabling you to add custom images, titles and descriptions for social media.\"]]],[1,\"p\",[[0,[],0,\"There’s no need to hard code your meta data. You can set your meta title and description using the post settings tool, which has a handy character guide and SERP preview. \"]]],[1,\"p\",[[0,[],0,\"Ghost will automatically implement structured data for your publication using JSON-LD to further optimise your content.\"]]],[10,0],[1,\"p\",[[0,[],0,\"You can test that the structured data \"],[0,[0],1,\"schema\"],[0,[],0,\" on your site is working as it should using \"],[0,[1],1,\"Google’s structured data tool\"],[0,[],0,\". \"]]],[1,\"h2\",[[0,[],0,\"Code Injection\"]]],[1,\"p\",[[0,[],0,\"This tool allows you to inject code on a per post or page basis, or across your entire site. This means you can modify CSS, add unique tracking codes, or add other scripts to the head or foot of your publication without making edits to your theme files. \"]]],[1,\"p\",[[0,[2],1,\"To add code site-wide\"],[0,[],0,\", use the code injection tool \"],[0,[3],1,\"in the main admin menu\"],[0,[],0,\". This is useful for adding a Facebook Pixel, a Google Analytics tracking code, or to start tracking with any other analytics tool.\"]]],[1,\"p\",[[0,[2],1,\"To add code to a post or page\"],[0,[],0,\", use the code injection tool within the post settings menu. This is useful if you want to add art direction, scripts or styles that are only applicable to one post or page. \"]]],[1,\"p\",[[0,[],0,\"From here, you might be interested in managing some more specific \"],[0,[4],1,\"admin settings\"],[0,[],0,\"!\"]]]]}", "featured": false, - "type": "post", + "page": false, "status": "published", "meta_title": null, "meta_description": null, @@ -550,7 +550,7 @@ "slug": "the-editor", "mobiledoc": "{\"version\":\"0.3.1\",\"atoms\":[],\"cards\":[[\"image\",{\"src\":\"https://static.ghost.org/v2.0.0/images/formatting-editor-demo.gif\"}],[\"code\",{\"code\":\"
\\n
\\n {{> \\\"site-nav\\\"}}\\n
\\n
\"}],[\"image\",{\"src\":\"https://static.ghost.org/v2.0.0/images/using-images-demo.gif\"}]],\"markups\":[[\"strong\"],[\"code\"],[\"a\",[\"href\",\"/publishing-options/\"]]],\"sections\":[[1,\"p\",[[0,[],0,\"Ghost has a powerful visual editor with familiar formatting options, as well as the ability to seamlessly add dynamic content. \"]]],[1,\"p\",[[0,[],0,\"Select the text to add formatting, headers or create links, or use Markdown shortcuts to do the work for you - if that's your thing. \"]]],[10,0],[1,\"h2\",[[0,[],0,\"Rich editing at your fingertips\"]]],[1,\"p\",[[0,[],0,\"The editor can also handle rich media objects, called \"],[0,[0],1,\"cards\"],[0,[],0,\". \"]]],[1,\"p\",[[0,[],0,\"You can insert a card either by clicking the \"],[0,[1],1,\"+\"],[0,[],0,\" button on a new line, or typing \"],[0,[1],1,\"/\"],[0,[],0,\" on a new line to search for a particular card. This allows you to efficiently insert\"],[0,[0],1,\" images\"],[0,[],0,\", \"],[0,[0],1,\"markdown\"],[0,[],0,\", \"],[0,[0],1,\"html\"],[0,[],0,\" and \"],[0,[0],1,\"embeds\"],[0,[],0,\".\"]]],[1,\"p\",[[0,[0],1,\"For Example\"],[0,[],0,\":\"]]],[3,\"ul\",[[[0,[],0,\"Insert a video from YouTube directly into your content by pasting the URL \"]],[[0,[],0,\"Create unique content like a button or content opt-in using the HTML card\"]],[[0,[],0,\"Need to share some code? Embed code blocks directly \"]]]],[10,1],[1,\"h1\",[[0,[],0,\"Working with images in posts\"]]],[1,\"p\",[[0,[],0,\"You can add images to your posts in many ways:\"]]],[3,\"ul\",[[[0,[],0,\"Upload from your computer\"]],[[0,[],0,\"Click and drag an image into the browser\"]],[[0,[],0,\"Paste directly into the editor from your clipboard\"]],[[0,[],0,\"Insert using a URL\"]]]],[1,\"p\",[[0,[],0,\"Once inserted you can blend images beautifully into your content at different sizes and add captions wherever needed.\"]]],[10,2],[1,\"p\",[[0,[],0,\"The post settings menu and publishing options can be found in the top right hand corner. For more advanced tips on post settings check out the \"],[0,[2],1,\"publishing options\"],[0,[],0,\" post!\"]]],[1,\"p\",[]]]}", "featured": false, - "type": "post", + "page": false, "status": "published", "meta_title": null, "meta_description": null, @@ -565,7 +565,7 @@ "slug": "welcome", "mobiledoc": "{\"version\":\"0.3.1\",\"atoms\":[],\"cards\":[],\"markups\":[[\"strong\"],[\"a\",[\"href\",\"https://ghost.org/downloads/\"]],[\"a\",[\"href\",\"https://ghost.org/pricing\"]],[\"a\",[\"href\",\"https://github.com/TryGhost\"]],[\"a\",[\"href\",\"/the-editor/\"]],[\"em\"]],\"sections\":[[1,\"p\",[[0,[],0,\"👋 Welcome, it's great to have you here.\"]]],[1,\"p\",[[0,[],0,\"We know that first impressions are important, so we've populated your new site with some initial \"],[0,[0],1,\"getting started\"],[0,[],0,\" posts that will help you get familiar with everything in no time. This is the first one!\"]]],[1,\"p\",[[0,[0],1,\"A few things you should know upfront\"],[0,[],0,\":\"]]],[3,\"ol\",[[[0,[],0,\"Ghost is designed for ambitious, professional publishers who want to actively build a business around their content. That's who it works best for. \"]],[[0,[],0,\"The entire platform can be modified and customised to suit your needs. It's very powerful, but does require some knowledge of code. Ghost is not necessarily a good platform for beginners or people who just want a simple personal blog. \"]],[[0,[],0,\"For the best experience we recommend downloading the \"],[0,[1],1,\"Ghost Desktop App\"],[0,[],0,\" for your computer, which is the best way to access your Ghost site on a desktop device. \"]]]],[1,\"p\",[[0,[],0,\"Ghost is made by an independent non-profit organisation called the Ghost Foundation. We are 100% self funded by revenue from our \"],[0,[2],1,\"Ghost(Pro)\"],[0,[],0,\" service, and every penny we make is re-invested into funding further development of free, open source technology for modern publishing.\"]]],[1,\"p\",[[0,[],0,\"The version of Ghost you are looking at right now would not have been made possible without generous contributions from the open source \"],[0,[3],1,\"community\"],[0,[],0,\".\"]]],[1,\"h2\",[[0,[],0,\"Next up, the editor\"]]],[1,\"p\",[[0,[],0,\"The main thing you'll want to read about next is probably: \"],[0,[4],1,\"the Ghost editor\"],[0,[],0,\". This is where the good stuff happens.\"]]],[1,\"blockquote\",[[0,[5],0,\"By the way, once you're done reading, you can simply delete the default \"],[0,[0],1,\"Ghost\"],[0,[],1,\" user from your team to remove all of these introductory posts! \"]]]]}", "featured": false, - "type": "post", + "page": false, "status": "published", "meta_title": null, "meta_description": null, diff --git a/core/server/data/validation/index.js b/core/server/data/validation/index.js index dd93ff061e..58136d3650 100644 --- a/core/server/data/validation/index.js +++ b/core/server/data/validation/index.js @@ -345,8 +345,7 @@ validate = function validate(value, key, validations, tableName) { } validationErrors.push(new common.errors.ValidationError({ - message: translation, - context: `${tableName}.${key}` + message: translation })); } diff --git a/core/server/models/base/index.js b/core/server/models/base/index.js index a945a9af9c..aa9fb4f441 100644 --- a/core/server/models/base/index.js +++ b/core/server/models/base/index.js @@ -668,11 +668,11 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({ case 'edit': return baseOptions.concat(extraOptions, ['id', 'require']); case 'findOne': - return baseOptions.concat(extraOptions, ['columns', 'require', 'mongoTransformer']); + return baseOptions.concat(extraOptions, ['columns', 'require']); case 'findAll': - return baseOptions.concat(extraOptions, ['columns', 'mongoTransformer']); + return baseOptions.concat(extraOptions, ['columns']); case 'findPage': - return baseOptions.concat(extraOptions, ['filter', 'order', 'page', 'limit', 'columns', 'mongoTransformer']); + return baseOptions.concat(extraOptions, ['filter', 'order', 'page', 'limit', 'columns']); default: return baseOptions.concat(extraOptions); } diff --git a/core/server/models/plugins/filter.js b/core/server/models/plugins/filter.js index 942227938e..c3d4b51c06 100644 --- a/core/server/models/plugins/filter.js +++ b/core/server/models/plugins/filter.js @@ -61,7 +61,6 @@ const filter = function filter(Bookshelf) { let extra = this.extraFilters(options); let overrides = this.enforcedFilters(options); let defaults = this.defaultFilters(options); - let transformer = options.mongoTransformer; debug('custom', custom); debug('extra', extra); @@ -82,8 +81,7 @@ const filter = function filter(Bookshelf) { relations: RELATIONS, expansions: EXPANSIONS, overrides: overrides, - defaults: defaults, - transformer: transformer + defaults: defaults }).querySQL(qb); }); } catch (err) { diff --git a/core/server/models/plugins/include-count.js b/core/server/models/plugins/include-count.js index 5d62241977..7535c1fe60 100644 --- a/core/server/models/plugins/include-count.js +++ b/core/server/models/plugins/include-count.js @@ -19,7 +19,7 @@ module.exports = function (Bookshelf) { if (options.context && options.context.public) { // @TODO use the filter behavior for posts - qb.andWhere('posts.type', '=', 'post'); + qb.andWhere('posts.page', '=', false); qb.andWhere('posts.status', '=', 'published'); } }); @@ -36,7 +36,7 @@ module.exports = function (Bookshelf) { if (options.context && options.context.public) { // @TODO use the filter behavior for posts - qb.andWhere('posts.type', '=', 'post'); + qb.andWhere('posts.page', '=', false); qb.andWhere('posts.status', '=', 'published'); } }); diff --git a/core/server/models/post.js b/core/server/models/post.js index a4a84b33d6..84164ef6ac 100644 --- a/core/server/models/post.js +++ b/core/server/models/post.js @@ -44,7 +44,7 @@ Post = ghostBookshelf.Model.extend({ uuid: uuid.v4(), status: 'draft', featured: false, - type: 'post', + page: false, visibility: 'public' }; }, @@ -74,10 +74,10 @@ Post = ghostBookshelf.Model.extend({ emitChange: function emitChange(event, options = {}) { let eventToTrigger; - let resourceType = this.get('type'); + let resourceType = this.get('page') ? 'page' : 'post'; if (options.usePreviousAttribute) { - resourceType = this.previous('type'); + resourceType = this.previous('page') ? 'page' : 'post'; } eventToTrigger = resourceType + '.' + event; @@ -118,7 +118,7 @@ Post = ghostBookshelf.Model.extend({ model.isScheduled = model.get('status') === 'scheduled'; model.wasPublished = model.previous('status') === 'published'; model.wasScheduled = model.previous('status') === 'scheduled'; - model.resourceTypeChanging = model.get('type') !== model.previous('type'); + model.resourceTypeChanging = model.get('page') !== model.previous('page'); model.publishedAtHasChanged = model.hasDateChanged('published_at'); model.needsReschedule = model.publishedAtHasChanged && model.isScheduled; @@ -567,7 +567,7 @@ Post = ghostBookshelf.Model.extend({ return null; } - return options.context && options.context.public ? 'type:post' : 'type:post+status:published'; + return options.context && options.context.public ? 'page:false' : 'page:false+status:published'; }, /** @@ -588,7 +588,7 @@ Post = ghostBookshelf.Model.extend({ options.staticPages = _.includes(['true', '1'], options.staticPages); } - filter = `page:${options.staticPages ? 'true' : 'false'}`; + filter = `page:${options.staticPages}`; } else if (options.staticPages === 'all') { filter = 'page:[true, false]'; } @@ -750,12 +750,6 @@ Post = ghostBookshelf.Model.extend({ edit: function edit(data, unfilteredOptions) { let options = this.filterOptions(unfilteredOptions, 'edit', {extraAllowedProperties: ['id']}); - // @TODO DELETE THIS (and the failing regression tests) when v0.1 is ded - if (Object.prototype.hasOwnProperty.call(data, 'page')) { - data.type = data.page ? 'page' : 'post'; - delete data.page; - } - const editPost = () => { options.forUpdate = true; diff --git a/core/server/services/slack.js b/core/server/services/slack.js index 277edb7beb..7413eaf8b8 100644 --- a/core/server/services/slack.js +++ b/core/server/services/slack.js @@ -45,7 +45,7 @@ function ping(post) { if (slackSettings && slackSettings.url && slackSettings.url !== '') { slackSettings.username = slackSettings.username ? slackSettings.username : 'Ghost'; // Only ping when not a page - if (post.type === 'page') { + if (post.page) { return; } diff --git a/core/server/services/xmlrpc.js b/core/server/services/xmlrpc.js index b46b4bc795..d8d5220418 100644 --- a/core/server/services/xmlrpc.js +++ b/core/server/services/xmlrpc.js @@ -27,7 +27,7 @@ function ping(post) { title = post.title, url = urlService.getUrlByResourceId(post.id, {absolute: true}); - if (post.type === 'page' || config.isPrivacyDisabled('useRpcPing') || settingsCache.get('is_private')) { + if (post.page || config.isPrivacyDisabled('useRpcPing') || settingsCache.get('is_private')) { return; } diff --git a/core/shared/nql-map-key-values/README.md b/core/shared/nql-map-key-values/README.md deleted file mode 100644 index 1bd406117f..0000000000 --- a/core/shared/nql-map-key-values/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# nql-map-key-values - -This utility returns a transformer which can be passed to the `@nexes/nql` library to transform queries - -### Usage - -```js -const nql = require('@nexes/nql'); -const mapKeyValues = require('nql-map-key-values'); - -nql('good:true', { - transformer: mapKeyValues({ - key: { - from: 'good', - to: 'bad' - }, - values: [{ - from: true, - to: false - }, { - from: false, - to: true - }] - }); -}).toJSON(); // => {bad: false} -``` diff --git a/core/shared/nql-map-key-values/index.js b/core/shared/nql-map-key-values/index.js deleted file mode 100644 index 5348c67d22..0000000000 --- a/core/shared/nql-map-key-values/index.js +++ /dev/null @@ -1,44 +0,0 @@ -const _ = require('lodash'); -const nql = require('@nexes/nql'); - -/* - * Returns the replacement value for input, or input if it doesn't exist - */ -function replaceValue(input, valueMappings) { - const replacer = valueMappings.find(({from}) => from === input); - return replacer && replacer.to || input; -} - -function fmap(item, fn) { - return Array.isArray(item) ? item.map(fn) : fn(item); -} - -function mapKeysAndValues(input, mapping) { - return nql.utils.mapQuery(input, function (value, key) { - // Ignore everything that has nothing to do with our mapping - if (key !== mapping.key.from) { - return { - [key]: value - }; - } - - // key: valueA - if (typeof value !== 'object') { - return { - [mapping.key.to]: replaceValue(value, mapping.values) - }; - } - - // key: { "$in": ['valueA', 'valueB'] } - // key: { "$ne": 'valueA' } - return { - [mapping.key.to]: _.reduce(value, (memo, objValue, objKey) => { - return Object.assign(memo, { - [objKey]: fmap(objValue, item => replaceValue(item, mapping.values)) - }); - }, {}) - }; - }); -} - -module.exports = mapping => input => mapKeysAndValues(input, mapping); diff --git a/core/shared/nql-map-key-values/package.json b/core/shared/nql-map-key-values/package.json deleted file mode 100644 index cc454e3e03..0000000000 --- a/core/shared/nql-map-key-values/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "nql-map-key-values", - "version": "0.0.0", - "description": "Map keys and values for nql query objects", - "main": "index.js", - "repository": "https://github.com/TryGhost/Ghost/tree/master/core/shared/nql-map-key-values", - "author": "Ghost Foundation", - "license": "MIT", - "private": true, - "dependencies": { - "@nexes/nql": "0.3.0", - "lodash": "4.17.15" - } -} diff --git a/core/test/acceptance/old/admin/pages_spec.js b/core/test/acceptance/old/admin/pages_spec.js index fd4aae77df..fa783e37f0 100644 --- a/core/test/acceptance/old/admin/pages_spec.js +++ b/core/test/acceptance/old/admin/pages_spec.js @@ -83,7 +83,7 @@ describe('Pages API', function () { .then((model) => { model.get('title').should.eql(page.title); model.get('status').should.eql(page.status); - model.get('type').should.eql('page'); + model.get('page').should.eql(true); }); }); @@ -116,7 +116,7 @@ describe('Pages API', function () { }, testUtils.context.internal); }) .then((model) => { - model.get('type').should.eql('page'); + model.get('page').should.eql(true); }); }); diff --git a/core/test/acceptance/old/admin/utils.js b/core/test/acceptance/old/admin/utils.js index 79b3ed38ea..3d122e157b 100644 --- a/core/test/acceptance/old/admin/utils.js +++ b/core/test/acceptance/old/admin/utils.js @@ -31,8 +31,6 @@ const expectedProperties = { .without('visibility') .without('locale') .without('page') - // v2 API doesn't return new type field - .without('type') // deprecated .without('author_id') // always returns computed properties @@ -48,8 +46,6 @@ const expectedProperties = { .without('visibility') .without('locale') .without('page') - // v2 API doesn't return new type field - .without('type') // deprecated .without('author_id') // always returns computed properties diff --git a/core/test/acceptance/old/content/utils.js b/core/test/acceptance/old/content/utils.js index d873f37e86..0b741533b5 100644 --- a/core/test/acceptance/old/content/utils.js +++ b/core/test/acceptance/old/content/utils.js @@ -23,8 +23,6 @@ const expectedProperties = { .without('locale', 'visibility') // These fields aren't useful as they always have known values .without('status') - // v2 API doesn't return new type field - .without('type') // @TODO: https://github.com/TryGhost/Ghost/issues/10335 // .without('page') // v2 returns a calculated excerpt field diff --git a/core/test/regression/api/canary/admin/schedules_spec.js b/core/test/regression/api/canary/admin/schedules_spec.js index ff6eaf5539..3b91d096ec 100644 --- a/core/test/regression/api/canary/admin/schedules_spec.js +++ b/core/test/regression/api/canary/admin/schedules_spec.js @@ -80,7 +80,7 @@ describe('Schedules API', function () { published_at: moment().add(30, 'seconds').toDate(), status: 'scheduled', slug: 'fifth', - type: 'page' + page: true })); return Promise.mapSeries(resources, function (post) { diff --git a/core/test/regression/api/canary/content/utils.js b/core/test/regression/api/canary/content/utils.js index 3c5cbc9c0e..8e807c0d95 100644 --- a/core/test/regression/api/canary/content/utils.js +++ b/core/test/regression/api/canary/content/utils.js @@ -25,7 +25,6 @@ const expectedProperties = { .without('status') // @TODO: https://github.com/TryGhost/Ghost/issues/10335 // .without('page') - .without('type') // canary returns a calculated excerpt field .concat('excerpt') , diff --git a/core/test/regression/api/v0.1/utils.js b/core/test/regression/api/v0.1/utils.js index 3652a12d51..d6bbde81b8 100644 --- a/core/test/regression/api/v0.1/utils.js +++ b/core/test/regression/api/v0.1/utils.js @@ -20,8 +20,6 @@ const expectedProperties = { .keys() // by default we only return html .without('mobiledoc', 'plaintext') - .without('type') - .concat('page') // swaps author_id to author, and always returns computed properties: url, comment_id, primary_tag, primary_author .without('author_id').concat('author', 'url', 'primary_tag', 'primary_author') .without('canonical_url') diff --git a/core/test/regression/api/v2/admin/schedules_spec.js b/core/test/regression/api/v2/admin/schedules_spec.js index c1385cd7ef..6a0abbffe2 100644 --- a/core/test/regression/api/v2/admin/schedules_spec.js +++ b/core/test/regression/api/v2/admin/schedules_spec.js @@ -80,7 +80,7 @@ describe('Schedules API', function () { published_at: moment().add(30, 'seconds').toDate(), status: 'scheduled', slug: 'fifth', - type: 'page' + page: true })); return Promise.mapSeries(resources, function (post) { diff --git a/core/test/regression/api/v2/content/utils.js b/core/test/regression/api/v2/content/utils.js index 2a7f12ace9..6da3f963f8 100644 --- a/core/test/regression/api/v2/content/utils.js +++ b/core/test/regression/api/v2/content/utils.js @@ -25,7 +25,6 @@ const expectedProperties = { .without('status') // @TODO: https://github.com/TryGhost/Ghost/issues/10335 // .without('page') - .without('type') // v2 returns a calculated excerpt field .concat('excerpt') , diff --git a/core/test/regression/models/model_posts_spec.js b/core/test/regression/models/model_posts_spec.js index 2be4568cf1..d6b4d2a7dc 100644 --- a/core/test/regression/models/model_posts_spec.js +++ b/core/test/regression/models/model_posts_spec.js @@ -532,7 +532,7 @@ describe('Post Model', function () { }).then(function (edited) { should.exist(edited); edited.attributes.status.should.equal('draft'); - edited.attributes.type.should.equal('page'); + edited.attributes.page.should.equal(true); Object.keys(eventsTriggered).length.should.eql(2); should.exist(eventsTriggered['post.deleted']); @@ -542,7 +542,7 @@ describe('Post Model', function () { }).then(function (edited) { should.exist(edited); edited.attributes.status.should.equal('draft'); - edited.attributes.type.should.equal('post'); + edited.attributes.page.should.equal(false); Object.keys(eventsTriggered).length.should.eql(4); should.exist(eventsTriggered['post.deleted']); @@ -569,7 +569,7 @@ describe('Post Model', function () { }).then(function (edited) { should.exist(edited); edited.attributes.status.should.equal('scheduled'); - edited.attributes.type.should.equal('page'); + edited.attributes.page.should.equal(true); Object.keys(eventsTriggered).length.should.eql(3); should.exist(eventsTriggered['post.deleted']); @@ -580,7 +580,7 @@ describe('Post Model', function () { }).then(function (edited) { should.exist(edited); edited.attributes.status.should.equal('scheduled'); - edited.attributes.type.should.equal('post'); + edited.attributes.page.should.equal(false); Object.keys(eventsTriggered).length.should.eql(7); should.exist(eventsTriggered['page.unscheduled']); @@ -606,7 +606,7 @@ describe('Post Model', function () { }).then(function (edited) { should.exist(edited); edited.attributes.status.should.equal('published'); - edited.attributes.type.should.equal('page'); + edited.attributes.page.should.equal(true); Object.keys(eventsTriggered).length.should.eql(4); should.exist(eventsTriggered['post.unpublished']); @@ -618,7 +618,7 @@ describe('Post Model', function () { }).then(function (edited) { should.exist(edited); edited.attributes.status.should.equal('published'); - edited.attributes.type.should.equal('post'); + edited.attributes.page.should.equal(false); Object.keys(eventsTriggered).length.should.eql(8); should.exist(eventsTriggered['page.unpublished']); @@ -644,7 +644,7 @@ describe('Post Model', function () { }).then(function (edited) { should.exist(edited); edited.attributes.status.should.equal('published'); - edited.attributes.type.should.equal('page'); + edited.attributes.page.should.equal(true); Object.keys(eventsTriggered).length.should.eql(5); should.exist(eventsTriggered['post.deleted']); @@ -657,7 +657,7 @@ describe('Post Model', function () { }).then(function (edited) { should.exist(edited); edited.attributes.status.should.equal('draft'); - edited.attributes.type.should.equal('post'); + edited.attributes.page.should.equal(false); Object.keys(eventsTriggered).length.should.eql(8); should.exist(eventsTriggered['page.unpublished']); @@ -1175,7 +1175,7 @@ describe('Post Model', function () { page = results.toJSON(); page.id.should.equal(firstItemData.id); page.status.should.equal('published'); - page.type.should.equal('page'); + page.page.should.be.true(); // Destroy the page return results.destroy(firstItemData); diff --git a/core/test/regression/site/site_spec.js b/core/test/regression/site/site_spec.js index b3c7d32635..cb7a767777 100644 --- a/core/test/regression/site/site_spec.js +++ b/core/test/regression/site/site_spec.js @@ -1915,7 +1915,7 @@ describe('Integration - Web - Site', function () { response.statusCode.should.eql(200); response.template.should.eql('tag'); - postSpy.args[0][0].options.filter.should.eql('(tags:\'bacon\'+tags.visibility:public)+type:post'); + postSpy.args[0][0].options.filter.should.eql('(tags:\'bacon\'+tags.visibility:public)+page:false'); postSpy.args[0][0].options.page.should.eql(1); postSpy.args[0][0].options.limit.should.eql(2); }); @@ -3659,7 +3659,7 @@ describe('Integration - Web - Site', function () { response.statusCode.should.eql(200); response.template.should.eql('tag'); - postSpy.args[0][0].options.filter.should.eql('(tags:\'bacon\'+tags.visibility:public)+type:post'); + postSpy.args[0][0].options.filter.should.eql('(tags:\'bacon\'+tags.visibility:public)+page:false'); postSpy.args[0][0].options.page.should.eql(1); postSpy.args[0][0].options.limit.should.eql(2); }); diff --git a/core/test/unit/api/canary/utils/serializers/input/pages_spec.js b/core/test/unit/api/canary/utils/serializers/input/pages_spec.js index ba4ff130b4..6e585812ba 100644 --- a/core/test/unit/api/canary/utils/serializers/input/pages_spec.js +++ b/core/test/unit/api/canary/utils/serializers/input/pages_spec.js @@ -13,7 +13,7 @@ describe('Unit: canary/utils/serializers/input/pages', function () { }; serializers.input.pages.browse(apiConfig, frame); - frame.options.filter.should.eql('type:page'); + frame.options.filter.should.eql('page:true'); }); it('combine filters', function () { @@ -27,7 +27,7 @@ describe('Unit: canary/utils/serializers/input/pages', function () { }; serializers.input.pages.browse(apiConfig, frame); - frame.options.filter.should.eql('(status:published+tag:eins)+type:page'); + frame.options.filter.should.eql('(status:published+tag:eins)+page:true'); }); it('combine filters', function () { @@ -41,7 +41,7 @@ describe('Unit: canary/utils/serializers/input/pages', function () { }; serializers.input.pages.browse(apiConfig, frame); - frame.options.filter.should.eql('(page:false+tag:eins)+type:page'); + frame.options.filter.should.eql('(page:false+tag:eins)+page:true'); }); it('combine filters', function () { @@ -55,7 +55,7 @@ describe('Unit: canary/utils/serializers/input/pages', function () { }; serializers.input.pages.browse(apiConfig, frame); - frame.options.filter.should.eql('(page:false)+type:page'); + frame.options.filter.should.eql('(page:false)+page:true'); }); it('remove mobiledoc option from formats', function () { @@ -87,7 +87,7 @@ describe('Unit: canary/utils/serializers/input/pages', function () { }; serializers.input.pages.read(apiConfig, frame); - frame.options.filter.should.eql('type:page'); + frame.options.filter.should.eql('page:true'); }); it('content api default', function () { @@ -107,7 +107,7 @@ describe('Unit: canary/utils/serializers/input/pages', function () { }; serializers.input.pages.read(apiConfig, frame); - frame.options.filter.should.eql('type:page'); + frame.options.filter.should.eql('page:true'); }); it('admin api default', function () { @@ -127,7 +127,7 @@ describe('Unit: canary/utils/serializers/input/pages', function () { }; serializers.input.pages.read(apiConfig, frame); - frame.options.filter.should.eql('(type:page)+status:[draft,published,scheduled]'); + frame.options.filter.should.eql('(page:true)+status:[draft,published,scheduled]'); }); it('custom page filter', function () { @@ -142,7 +142,7 @@ describe('Unit: canary/utils/serializers/input/pages', function () { }; serializers.input.pages.read(apiConfig, frame); - frame.options.filter.should.eql('(page:false)+type:page'); + frame.options.filter.should.eql('(page:false)+page:true'); }); it('custom status filter', function () { @@ -163,7 +163,7 @@ describe('Unit: canary/utils/serializers/input/pages', function () { }; serializers.input.pages.read(apiConfig, frame); - frame.options.filter.should.eql('(status:draft)+type:page'); + frame.options.filter.should.eql('(status:draft)+page:true'); }); it('remove mobiledoc option from formats', function () { diff --git a/core/test/unit/api/canary/utils/serializers/input/posts_spec.js b/core/test/unit/api/canary/utils/serializers/input/posts_spec.js index 530d0fd165..0e93a70446 100644 --- a/core/test/unit/api/canary/utils/serializers/input/posts_spec.js +++ b/core/test/unit/api/canary/utils/serializers/input/posts_spec.js @@ -21,7 +21,7 @@ describe('Unit: canary/utils/serializers/input/posts', function () { }; serializers.input.posts.browse(apiConfig, frame); - frame.options.filter.should.eql('type:post'); + frame.options.filter.should.eql('page:false'); }); it('should not work for non public context', function () { @@ -36,7 +36,7 @@ describe('Unit: canary/utils/serializers/input/posts', function () { }; serializers.input.posts.browse(apiConfig, frame); - should.equal(frame.options.filter, '(type:post)+status:[draft,published,scheduled]'); + should.equal(frame.options.filter, '(page:false)+status:[draft,published,scheduled]'); }); it('combine filters', function () { @@ -56,7 +56,7 @@ describe('Unit: canary/utils/serializers/input/posts', function () { }; serializers.input.posts.browse(apiConfig, frame); - frame.options.filter.should.eql('(status:published+tag:eins)+type:post'); + frame.options.filter.should.eql('(status:published+tag:eins)+page:false'); }); it('combine filters', function () { @@ -76,7 +76,7 @@ describe('Unit: canary/utils/serializers/input/posts', function () { }; serializers.input.posts.browse(apiConfig, frame); - frame.options.filter.should.eql('(page:true+tag:eins)+type:post'); + frame.options.filter.should.eql('(page:true+tag:eins)+page:false'); }); it('combine filters', function () { @@ -96,7 +96,7 @@ describe('Unit: canary/utils/serializers/input/posts', function () { }; serializers.input.posts.browse(apiConfig, frame); - frame.options.filter.should.eql('(page:true)+type:post'); + frame.options.filter.should.eql('(page:true)+page:false'); }); it('combine filters', function () { @@ -116,7 +116,7 @@ describe('Unit: canary/utils/serializers/input/posts', function () { }; serializers.input.posts.browse(apiConfig, frame); - frame.options.filter.should.eql('((page:true,page:false))+type:post'); + frame.options.filter.should.eql('((page:true,page:false))+page:false'); }); it('remove mobiledoc option from formats', function () { @@ -137,7 +137,7 @@ describe('Unit: canary/utils/serializers/input/posts', function () { }); describe('read', function () { - it('with apiType of "content" it forces type filter', function () { + it('with apiType of "content" it forces page filter', function () { const apiConfig = {}; const frame = { apiType: 'content', @@ -146,24 +146,24 @@ describe('Unit: canary/utils/serializers/input/posts', function () { }; serializers.input.posts.read(apiConfig, frame); - frame.options.filter.should.eql('type:post'); + frame.options.filter.should.eql('page:false'); }); - it('with apiType of "content" it forces type:post filter', function () { + it('with apiType of "content" it forces page false filter', function () { const apiConfig = {}; const frame = { apiType: 'content', options: { - filter: 'type:page' + filter: 'page:true' }, data: {} }; serializers.input.posts.read(apiConfig, frame); - frame.options.filter.should.eql('(type:page)+type:post'); + frame.options.filter.should.eql('(page:true)+page:false'); }); - it('with apiType of "admin" it forces type & status false filter', function () { + it('with apiType of "admin" it forces page & status false filter', function () { const apiConfig = {}; const frame = { apiType: 'admin', @@ -179,10 +179,10 @@ describe('Unit: canary/utils/serializers/input/posts', function () { }; serializers.input.posts.read(apiConfig, frame); - frame.options.filter.should.eql('(type:post)+status:[draft,published,scheduled]'); + frame.options.filter.should.eql('(page:false)+status:[draft,published,scheduled]'); }); - it('with apiType of "admin" it forces type:post filter & respects custom status filter', function () { + it('with apiType of "admin" it forces page filter & respects custom status filter', function () { const apiConfig = {}; const frame = { apiType: 'admin', @@ -199,7 +199,7 @@ describe('Unit: canary/utils/serializers/input/posts', function () { }; serializers.input.posts.read(apiConfig, frame); - frame.options.filter.should.eql('(status:draft)+type:post'); + frame.options.filter.should.eql('(status:draft)+page:false'); }); it('remove mobiledoc option from formats', function () { diff --git a/core/test/unit/api/v2/utils/serializers/input/pages_spec.js b/core/test/unit/api/v2/utils/serializers/input/pages_spec.js index 23b3254115..b51f9a1efa 100644 --- a/core/test/unit/api/v2/utils/serializers/input/pages_spec.js +++ b/core/test/unit/api/v2/utils/serializers/input/pages_spec.js @@ -13,7 +13,7 @@ describe('Unit: v2/utils/serializers/input/pages', function () { }; serializers.input.pages.browse(apiConfig, frame); - frame.options.filter.should.eql('type:page'); + frame.options.filter.should.eql('page:true'); }); it('combine filters', function () { @@ -27,7 +27,7 @@ describe('Unit: v2/utils/serializers/input/pages', function () { }; serializers.input.pages.browse(apiConfig, frame); - frame.options.filter.should.eql('(status:published+tag:eins)+type:page'); + frame.options.filter.should.eql('(status:published+tag:eins)+page:true'); }); it('combine filters', function () { @@ -41,7 +41,7 @@ describe('Unit: v2/utils/serializers/input/pages', function () { }; serializers.input.pages.browse(apiConfig, frame); - frame.options.filter.should.eql('(page:false+tag:eins)+type:page'); + frame.options.filter.should.eql('(page:false+tag:eins)+page:true'); }); it('combine filters', function () { @@ -55,7 +55,7 @@ describe('Unit: v2/utils/serializers/input/pages', function () { }; serializers.input.pages.browse(apiConfig, frame); - frame.options.filter.should.eql('(page:false)+type:page'); + frame.options.filter.should.eql('(page:false)+page:true'); }); it('remove mobiledoc option from formats', function () { @@ -87,7 +87,7 @@ describe('Unit: v2/utils/serializers/input/pages', function () { }; serializers.input.pages.read(apiConfig, frame); - frame.options.filter.should.eql('type:page'); + frame.options.filter.should.eql('page:true'); }); it('content api default', function () { @@ -107,7 +107,7 @@ describe('Unit: v2/utils/serializers/input/pages', function () { }; serializers.input.pages.read(apiConfig, frame); - frame.options.filter.should.eql('type:page'); + frame.options.filter.should.eql('page:true'); }); it('admin api default', function () { @@ -127,7 +127,7 @@ describe('Unit: v2/utils/serializers/input/pages', function () { }; serializers.input.pages.read(apiConfig, frame); - frame.options.filter.should.eql('(type:page)+status:[draft,published,scheduled]'); + frame.options.filter.should.eql('(page:true)+status:[draft,published,scheduled]'); }); it('custom page filter', function () { @@ -142,7 +142,7 @@ describe('Unit: v2/utils/serializers/input/pages', function () { }; serializers.input.pages.read(apiConfig, frame); - frame.options.filter.should.eql('(page:false)+type:page'); + frame.options.filter.should.eql('(page:false)+page:true'); }); it('custom status filter', function () { @@ -163,7 +163,7 @@ describe('Unit: v2/utils/serializers/input/pages', function () { }; serializers.input.pages.read(apiConfig, frame); - frame.options.filter.should.eql('(status:draft)+type:page'); + frame.options.filter.should.eql('(status:draft)+page:true'); }); it('remove mobiledoc option from formats', function () { diff --git a/core/test/unit/api/v2/utils/serializers/input/posts_spec.js b/core/test/unit/api/v2/utils/serializers/input/posts_spec.js index 42168608a5..257e6498f7 100644 --- a/core/test/unit/api/v2/utils/serializers/input/posts_spec.js +++ b/core/test/unit/api/v2/utils/serializers/input/posts_spec.js @@ -21,7 +21,7 @@ describe('Unit: v2/utils/serializers/input/posts', function () { }; serializers.input.posts.browse(apiConfig, frame); - frame.options.filter.should.eql('type:post'); + frame.options.filter.should.eql('page:false'); }); it('should not work for non public context', function () { @@ -36,7 +36,7 @@ describe('Unit: v2/utils/serializers/input/posts', function () { }; serializers.input.posts.browse(apiConfig, frame); - should.equal(frame.options.filter, '(type:post)+status:[draft,published,scheduled]'); + should.equal(frame.options.filter, '(page:false)+status:[draft,published,scheduled]'); }); it('combine filters', function () { @@ -56,7 +56,7 @@ describe('Unit: v2/utils/serializers/input/posts', function () { }; serializers.input.posts.browse(apiConfig, frame); - frame.options.filter.should.eql('(status:published+tag:eins)+type:post'); + frame.options.filter.should.eql('(status:published+tag:eins)+page:false'); }); it('combine filters', function () { @@ -76,7 +76,7 @@ describe('Unit: v2/utils/serializers/input/posts', function () { }; serializers.input.posts.browse(apiConfig, frame); - frame.options.filter.should.eql('(page:true+tag:eins)+type:post'); + frame.options.filter.should.eql('(page:true+tag:eins)+page:false'); }); it('combine filters', function () { @@ -96,7 +96,7 @@ describe('Unit: v2/utils/serializers/input/posts', function () { }; serializers.input.posts.browse(apiConfig, frame); - frame.options.filter.should.eql('(page:true)+type:post'); + frame.options.filter.should.eql('(page:true)+page:false'); }); it('combine filters', function () { @@ -116,7 +116,7 @@ describe('Unit: v2/utils/serializers/input/posts', function () { }; serializers.input.posts.browse(apiConfig, frame); - frame.options.filter.should.eql('((page:true,page:false))+type:post'); + frame.options.filter.should.eql('((page:true,page:false))+page:false'); }); it('remove mobiledoc option from formats', function () { @@ -137,7 +137,7 @@ describe('Unit: v2/utils/serializers/input/posts', function () { }); describe('read', function () { - it('with apiType of "content" it forces type filter', function () { + it('with apiType of "content" it forces page filter', function () { const apiConfig = {}; const frame = { apiType: 'content', @@ -146,24 +146,24 @@ describe('Unit: v2/utils/serializers/input/posts', function () { }; serializers.input.posts.read(apiConfig, frame); - frame.options.filter.should.eql('type:post'); + frame.options.filter.should.eql('page:false'); }); - it('with apiType of "content" it forces type:post filter', function () { + it('with apiType of "content" it forces page false filter', function () { const apiConfig = {}; const frame = { apiType: 'content', options: { - filter: 'type:page' + filter: 'page:true' }, data: {} }; serializers.input.posts.read(apiConfig, frame); - frame.options.filter.should.eql('(type:page)+type:post'); + frame.options.filter.should.eql('(page:true)+page:false'); }); - it('with apiType of "admin" it forces type & status false filter', function () { + it('with apiType of "admin" it forces page & status false filter', function () { const apiConfig = {}; const frame = { apiType: 'admin', @@ -179,10 +179,10 @@ describe('Unit: v2/utils/serializers/input/posts', function () { }; serializers.input.posts.read(apiConfig, frame); - frame.options.filter.should.eql('(type:post)+status:[draft,published,scheduled]'); + frame.options.filter.should.eql('(page:false)+status:[draft,published,scheduled]'); }); - it('with apiType of "admin" it forces type:post filter & respects custom status filter', function () { + it('with apiType of "admin" it forces page filter & respects custom status filter', function () { const apiConfig = {}; const frame = { apiType: 'admin', @@ -199,7 +199,7 @@ describe('Unit: v2/utils/serializers/input/posts', function () { }; serializers.input.posts.read(apiConfig, frame); - frame.options.filter.should.eql('(status:draft)+type:post'); + frame.options.filter.should.eql('(status:draft)+page:false'); }); it('remove mobiledoc option from formats', function () { diff --git a/core/test/unit/data/importer/importers/data/posts_spec.js b/core/test/unit/data/importer/importers/data/posts_spec.js index 0bb8e0cc70..18aeef2ce8 100644 --- a/core/test/unit/data/importer/importers/data/posts_spec.js +++ b/core/test/unit/data/importer/importers/data/posts_spec.js @@ -4,7 +4,7 @@ const PostsImporter = require('../../../../../../server/data/importer/importers/ describe('PostsImporter', function () { describe('#beforeImport', function () { - it('converts post.page to post.type', function () { + it('converts post.type to post.page', function () { const fakePosts = [{ slug: 'page-false', page: false @@ -25,67 +25,27 @@ describe('PostsImporter', function () { const pageFalse = find(importer.dataToImport, {slug: 'page-false'}); should.exist(pageFalse); - should.not.exist(pageFalse.page, 'pageFalse.page should not exist'); - should.exist(pageFalse.type, 'pageFalse.type should exist'); - pageFalse.type.should.equal('post'); + should.exist(pageFalse.page, 'pageFalse.page should exist'); + should.not.exist(pageFalse.type, 'pageFalse.type should not exist'); + pageFalse.page.should.equal(false); const pageTrue = find(importer.dataToImport, {slug: 'page-true'}); should.exist(pageTrue); - should.not.exist(pageTrue.page, 'pageTrue.page should not exist'); - should.exist(pageTrue.type, 'pageTrue.type should exist'); - pageTrue.type.should.equal('page'); + should.exist(pageTrue.page, 'pageTrue.page should exist'); + should.not.exist(pageTrue.type, 'pageTrue.type should not exist'); + pageTrue.page.should.equal(true); const typePost = find(importer.dataToImport, {slug: 'type-post'}); should.exist(typePost); - should.not.exist(typePost.page, 'typePost.page should not exist'); - should.exist(typePost.type, 'typePost.type should exist'); - typePost.type.should.equal('post'); + should.exist(typePost.page, 'typePost.page should exist'); + should.not.exist(typePost.type, 'typePost.type should not exist'); + typePost.page.should.equal(false); const typePage = find(importer.dataToImport, {slug: 'type-page'}); should.exist(typePage); - should.not.exist(typePage.page, 'typePage.page should not exist'); - should.exist(typePage.type, 'typePage.type should exist'); - typePage.type.should.equal('page'); - }); - - it('gives precedence to post.type when post.page is also present', function () { - const fakePosts = [{ - slug: 'page-false-type-page', - page: false, - type: 'page' - }, { - slug: 'page-true-type-page', - page: true, - type: 'page' - }, { - slug: 'page-false-type-post', - page: false, - type: 'post' - }, { - slug: 'page-true-type-post', - page: true, - type: 'post' - }]; - - const importer = new PostsImporter({posts: fakePosts}); - - importer.beforeImport(); - - const pageFalseTypePage = find(importer.dataToImport, {slug: 'page-false-type-page'}); - should.exist(pageFalseTypePage); - pageFalseTypePage.type.should.equal('page', 'pageFalseTypePage.type'); - - const pageTrueTypePage = find(importer.dataToImport, {slug: 'page-true-type-page'}); - should.exist(pageTrueTypePage); - pageTrueTypePage.type.should.equal('page', 'pageTrueTypePage.type'); - - const pageFalseTypePost = find(importer.dataToImport, {slug: 'page-false-type-post'}); - should.exist(pageFalseTypePost); - pageFalseTypePost.type.should.equal('post', 'pageFalseTypePost.type'); - - const pageTrueTypePost = find(importer.dataToImport, {slug: 'page-true-type-post'}); - should.exist(pageTrueTypePost); - pageTrueTypePost.type.should.equal('post', 'pageTrueTypePost.type'); + should.exist(typePage.page, 'typePage.page should exist'); + should.not.exist(typePage.type, 'typePage.type should not exist'); + typePage.page.should.equal(true); }); }); }); diff --git a/core/test/unit/data/schema/integrity_spec.js b/core/test/unit/data/schema/integrity_spec.js index b9396d623c..ad73112448 100644 --- a/core/test/unit/data/schema/integrity_spec.js +++ b/core/test/unit/data/schema/integrity_spec.js @@ -19,8 +19,8 @@ var should = require('should'), */ describe('DB version integrity', function () { // Only these variables should need updating - const currentSchemaHash = '6b747746e8bcaa9532b69a2f72d0d577'; - const currentFixturesHash = 'd8e9f2d22a7c0714b301ad26bb735729'; + const currentSchemaHash = 'fda0398e93a74b2dc435cb4c026679ba'; + const currentFixturesHash = 'c7b485fe2f16517295bd35c761129729'; // If this test is failing, then it is likely a change has been made that requires a DB version bump, // and the values above will need updating as confirmation diff --git a/core/test/unit/data/validation/index_spec.js b/core/test/unit/data/validation/index_spec.js index acb5724f9c..79cd3e2c23 100644 --- a/core/test/unit/data/validation/index_spec.js +++ b/core/test/unit/data/validation/index_spec.js @@ -81,22 +81,26 @@ describe('Validation', function () { }); it('transforms 0 and 1', function () { - const post = models.Post.forge(testUtils.DataGenerator.forKnex.createPost({slug: 'test', featured: 0})); + const post = models.Post.forge(testUtils.DataGenerator.forKnex.createPost({slug: 'test', featured: 0, page: 1})); post.get('featured').should.eql(0); + post.get('page').should.eql(1); return validation.validateSchema('posts', post, {method: 'insert'}) .then(function () { post.get('featured').should.eql(false); + post.get('page').should.eql(true); }); }); it('keeps true or false', function () { - const post = models.Post.forge(testUtils.DataGenerator.forKnex.createPost({slug: 'test', featured: true})); + const post = models.Post.forge(testUtils.DataGenerator.forKnex.createPost({slug: 'test', featured: true, page: false})); post.get('featured').should.eql(true); + post.get('page').should.eql(false); return validation.validateSchema('posts', post, {method: 'insert'}) .then(function () { post.get('featured').should.eql(true); + post.get('page').should.eql(false); }); }); }); diff --git a/core/test/unit/models/post_spec.js b/core/test/unit/models/post_spec.js index 946b92a65a..46c14a9a15 100644 --- a/core/test/unit/models/post_spec.js +++ b/core/test/unit/models/post_spec.js @@ -45,21 +45,21 @@ describe('Unit: models/post', function () { withRelated: ['tags'] }).then(() => { queries.length.should.eql(2); - queries[0].sql.should.eql('select count(distinct posts.id) as aggregate from `posts` where ((`posts`.`id` != ? and `posts`.`id` in (select `posts_tags`.`post_id` from `posts_tags` inner join `tags` on `tags`.`id` = `posts_tags`.`tag_id` where `tags`.`slug` in (?, ?))) and (`posts`.`type` = ? and `posts`.`status` = ?))'); + queries[0].sql.should.eql('select count(distinct posts.id) as aggregate from `posts` where ((`posts`.`id` != ? and `posts`.`id` in (select `posts_tags`.`post_id` from `posts_tags` inner join `tags` on `tags`.`id` = `posts_tags`.`tag_id` where `tags`.`slug` in (?, ?))) and (`posts`.`page` = ? and `posts`.`status` = ?))'); queries[0].bindings.should.eql([ testUtils.filterData.data.posts[3].id, 'photo', 'video', - 'post', + false, 'published' ]); - queries[1].sql.should.eql('select `posts`.* from `posts` where ((`posts`.`id` != ? and `posts`.`id` in (select `posts_tags`.`post_id` from `posts_tags` inner join `tags` on `tags`.`id` = `posts_tags`.`tag_id` where `tags`.`slug` in (?, ?))) and (`posts`.`type` = ? and `posts`.`status` = ?)) order by (SELECT count(*) FROM posts_tags WHERE post_id = posts.id) DESC, CASE WHEN posts.status = \'scheduled\' THEN 1 WHEN posts.status = \'draft\' THEN 2 ELSE 3 END ASC,CASE WHEN posts.status != \'draft\' THEN posts.published_at END DESC,posts.updated_at DESC,posts.id DESC limit ?'); + queries[1].sql.should.eql('select `posts`.* from `posts` where ((`posts`.`id` != ? and `posts`.`id` in (select `posts_tags`.`post_id` from `posts_tags` inner join `tags` on `tags`.`id` = `posts_tags`.`tag_id` where `tags`.`slug` in (?, ?))) and (`posts`.`page` = ? and `posts`.`status` = ?)) order by (SELECT count(*) FROM posts_tags WHERE post_id = posts.id) DESC, CASE WHEN posts.status = \'scheduled\' THEN 1 WHEN posts.status = \'draft\' THEN 2 ELSE 3 END ASC,CASE WHEN posts.status != \'draft\' THEN posts.published_at END DESC,posts.updated_at DESC,posts.id DESC limit ?'); queries[1].bindings.should.eql([ testUtils.filterData.data.posts[3].id, 'photo', 'video', - 'post', + false, 'published', 3 ]); @@ -80,21 +80,21 @@ describe('Unit: models/post', function () { withRelated: ['authors', 'tags'] }).then(() => { queries.length.should.eql(2); - queries[0].sql.should.eql('select count(distinct posts.id) as aggregate from `posts` where (((`posts`.`feature_image` is not null or `posts`.`id` in (select `posts_tags`.`post_id` from `posts_tags` inner join `tags` on `tags`.`id` = `posts_tags`.`tag_id` where `tags`.`slug` = ?)) and `posts`.`id` in (select `posts_authors`.`post_id` from `posts_authors` inner join `users` as `authors` on `authors`.`id` = `posts_authors`.`author_id` where `authors`.`slug` in (?, ?))) and (`posts`.`type` = ? and `posts`.`status` = ?))'); + queries[0].sql.should.eql('select count(distinct posts.id) as aggregate from `posts` where (((`posts`.`feature_image` is not null or `posts`.`id` in (select `posts_tags`.`post_id` from `posts_tags` inner join `tags` on `tags`.`id` = `posts_tags`.`tag_id` where `tags`.`slug` = ?)) and `posts`.`id` in (select `posts_authors`.`post_id` from `posts_authors` inner join `users` as `authors` on `authors`.`id` = `posts_authors`.`author_id` where `authors`.`slug` in (?, ?))) and (`posts`.`page` = ? and `posts`.`status` = ?))'); queries[0].bindings.should.eql([ 'hash-audio', 'leslie', 'pat', - 'post', + false, 'published' ]); - queries[1].sql.should.eql('select `posts`.* from `posts` where (((`posts`.`feature_image` is not null or `posts`.`id` in (select `posts_tags`.`post_id` from `posts_tags` inner join `tags` on `tags`.`id` = `posts_tags`.`tag_id` where `tags`.`slug` = ?)) and `posts`.`id` in (select `posts_authors`.`post_id` from `posts_authors` inner join `users` as `authors` on `authors`.`id` = `posts_authors`.`author_id` where `authors`.`slug` in (?, ?))) and (`posts`.`type` = ? and `posts`.`status` = ?)) order by (SELECT count(*) FROM posts_authors WHERE post_id = posts.id) DESC, CASE WHEN posts.status = \'scheduled\' THEN 1 WHEN posts.status = \'draft\' THEN 2 ELSE 3 END ASC,CASE WHEN posts.status != \'draft\' THEN posts.published_at END DESC,posts.updated_at DESC,posts.id DESC limit ?'); + queries[1].sql.should.eql('select `posts`.* from `posts` where (((`posts`.`feature_image` is not null or `posts`.`id` in (select `posts_tags`.`post_id` from `posts_tags` inner join `tags` on `tags`.`id` = `posts_tags`.`tag_id` where `tags`.`slug` = ?)) and `posts`.`id` in (select `posts_authors`.`post_id` from `posts_authors` inner join `users` as `authors` on `authors`.`id` = `posts_authors`.`author_id` where `authors`.`slug` in (?, ?))) and (`posts`.`page` = ? and `posts`.`status` = ?)) order by (SELECT count(*) FROM posts_authors WHERE post_id = posts.id) DESC, CASE WHEN posts.status = \'scheduled\' THEN 1 WHEN posts.status = \'draft\' THEN 2 ELSE 3 END ASC,CASE WHEN posts.status != \'draft\' THEN posts.published_at END DESC,posts.updated_at DESC,posts.id DESC limit ?'); queries[1].bindings.should.eql([ 'hash-audio', 'leslie', 'pat', - 'post', + false, 'published', 15 ]); @@ -116,17 +116,17 @@ describe('Unit: models/post', function () { withRelated: ['tags'] }).then(() => { queries.length.should.eql(2); - queries[0].sql.should.eql('select count(distinct posts.id) as aggregate from `posts` where (`posts`.`published_at` > ? and (`posts`.`type` = ? and `posts`.`status` = ?))'); + queries[0].sql.should.eql('select count(distinct posts.id) as aggregate from `posts` where (`posts`.`published_at` > ? and (`posts`.`page` = ? and `posts`.`status` = ?))'); queries[0].bindings.should.eql([ '2015-07-20', - 'post', + false, 'published' ]); - queries[1].sql.should.eql('select `posts`.* from `posts` where (`posts`.`published_at` > ? and (`posts`.`type` = ? and `posts`.`status` = ?)) order by CASE WHEN posts.status = \'scheduled\' THEN 1 WHEN posts.status = \'draft\' THEN 2 ELSE 3 END ASC,CASE WHEN posts.status != \'draft\' THEN posts.published_at END DESC,posts.updated_at DESC,posts.id DESC limit ?'); + queries[1].sql.should.eql('select `posts`.* from `posts` where (`posts`.`published_at` > ? and (`posts`.`page` = ? and `posts`.`status` = ?)) order by CASE WHEN posts.status = \'scheduled\' THEN 1 WHEN posts.status = \'draft\' THEN 2 ELSE 3 END ASC,CASE WHEN posts.status != \'draft\' THEN posts.published_at END DESC,posts.updated_at DESC,posts.id DESC limit ?'); queries[1].bindings.should.eql([ '2015-07-20', - 'post', + false, 'published', 5 ]); @@ -148,19 +148,19 @@ describe('Unit: models/post', function () { withRelated: ['tags'] }).then(() => { queries.length.should.eql(2); - queries[0].sql.should.eql('select count(distinct posts.id) as aggregate from `posts` where ((`posts`.`id` in (select `posts_tags`.`post_id` from `posts_tags` inner join `tags` on `tags`.`id` = `posts_tags`.`tag_id` and `posts_tags`.`sort_order` = 0 where `tags`.`slug` = ? and `tags`.`visibility` = ?)) and (`posts`.`type` = ? and `posts`.`status` = ?))'); + queries[0].sql.should.eql('select count(distinct posts.id) as aggregate from `posts` where ((`posts`.`id` in (select `posts_tags`.`post_id` from `posts_tags` inner join `tags` on `tags`.`id` = `posts_tags`.`tag_id` and `posts_tags`.`sort_order` = 0 where `tags`.`slug` = ? and `tags`.`visibility` = ?)) and (`posts`.`page` = ? and `posts`.`status` = ?))'); queries[0].bindings.should.eql([ 'photo', 'public', - 'post', + false, 'published' ]); - queries[1].sql.should.eql('select `posts`.* from `posts` where ((`posts`.`id` in (select `posts_tags`.`post_id` from `posts_tags` inner join `tags` on `tags`.`id` = `posts_tags`.`tag_id` and `posts_tags`.`sort_order` = 0 where `tags`.`slug` = ? and `tags`.`visibility` = ?)) and (`posts`.`type` = ? and `posts`.`status` = ?)) order by CASE WHEN posts.status = \'scheduled\' THEN 1 WHEN posts.status = \'draft\' THEN 2 ELSE 3 END ASC,CASE WHEN posts.status != \'draft\' THEN posts.published_at END DESC,posts.updated_at DESC,posts.id DESC limit ?'); + queries[1].sql.should.eql('select `posts`.* from `posts` where ((`posts`.`id` in (select `posts_tags`.`post_id` from `posts_tags` inner join `tags` on `tags`.`id` = `posts_tags`.`tag_id` and `posts_tags`.`sort_order` = 0 where `tags`.`slug` = ? and `tags`.`visibility` = ?)) and (`posts`.`page` = ? and `posts`.`status` = ?)) order by CASE WHEN posts.status = \'scheduled\' THEN 1 WHEN posts.status = \'draft\' THEN 2 ELSE 3 END ASC,CASE WHEN posts.status != \'draft\' THEN posts.published_at END DESC,posts.updated_at DESC,posts.id DESC limit ?'); queries[1].bindings.should.eql([ 'photo', 'public', - 'post', + false, 'published', 15 ]); @@ -181,19 +181,19 @@ describe('Unit: models/post', function () { withRelated: ['authors'] }).then(() => { queries.length.should.eql(2); - queries[0].sql.should.eql('select count(distinct posts.id) as aggregate from `posts` where ((`posts`.`id` in (select `posts_authors`.`post_id` from `posts_authors` inner join `users` as `authors` on `authors`.`id` = `posts_authors`.`author_id` and `posts_authors`.`sort_order` = 0 where `authors`.`slug` = ? and `authors`.`visibility` = ?)) and (`posts`.`type` = ? and `posts`.`status` = ?))'); + queries[0].sql.should.eql('select count(distinct posts.id) as aggregate from `posts` where ((`posts`.`id` in (select `posts_authors`.`post_id` from `posts_authors` inner join `users` as `authors` on `authors`.`id` = `posts_authors`.`author_id` and `posts_authors`.`sort_order` = 0 where `authors`.`slug` = ? and `authors`.`visibility` = ?)) and (`posts`.`page` = ? and `posts`.`status` = ?))'); queries[0].bindings.should.eql([ 'leslie', 'public', - 'post', + false, 'published' ]); - queries[1].sql.should.eql('select `posts`.* from `posts` where ((`posts`.`id` in (select `posts_authors`.`post_id` from `posts_authors` inner join `users` as `authors` on `authors`.`id` = `posts_authors`.`author_id` and `posts_authors`.`sort_order` = 0 where `authors`.`slug` = ? and `authors`.`visibility` = ?)) and (`posts`.`type` = ? and `posts`.`status` = ?)) order by CASE WHEN posts.status = \'scheduled\' THEN 1 WHEN posts.status = \'draft\' THEN 2 ELSE 3 END ASC,CASE WHEN posts.status != \'draft\' THEN posts.published_at END DESC,posts.updated_at DESC,posts.id DESC limit ?'); + queries[1].sql.should.eql('select `posts`.* from `posts` where ((`posts`.`id` in (select `posts_authors`.`post_id` from `posts_authors` inner join `users` as `authors` on `authors`.`id` = `posts_authors`.`author_id` and `posts_authors`.`sort_order` = 0 where `authors`.`slug` = ? and `authors`.`visibility` = ?)) and (`posts`.`page` = ? and `posts`.`status` = ?)) order by CASE WHEN posts.status = \'scheduled\' THEN 1 WHEN posts.status = \'draft\' THEN 2 ELSE 3 END ASC,CASE WHEN posts.status != \'draft\' THEN posts.published_at END DESC,posts.updated_at DESC,posts.id DESC limit ?'); queries[1].bindings.should.eql([ 'leslie', 'public', - 'post', + false, 'published', 15 ]); @@ -224,20 +224,20 @@ describe('Unit: models/post', function () { } }).then(() => { queries.length.should.eql(2); - queries[0].sql.should.eql('select count(distinct posts.id) as aggregate from `posts` where ((`posts`.`status` in (?, ?) and `posts`.`status` = ?) and (`posts`.`type` = ?))'); + queries[0].sql.should.eql('select count(distinct posts.id) as aggregate from `posts` where ((`posts`.`status` in (?, ?) and `posts`.`status` = ?) and (`posts`.`page` = ?))'); queries[0].bindings.should.eql([ 'published', 'draft', 'published', - 'post' + false ]); - queries[1].sql.should.eql('select `posts`.* from `posts` where ((`posts`.`status` in (?, ?) and `posts`.`status` = ?) and (`posts`.`type` = ?)) order by CASE WHEN posts.status = \'scheduled\' THEN 1 WHEN posts.status = \'draft\' THEN 2 ELSE 3 END ASC,CASE WHEN posts.status != \'draft\' THEN posts.published_at END DESC,posts.updated_at DESC,posts.id DESC'); + queries[1].sql.should.eql('select `posts`.* from `posts` where ((`posts`.`status` in (?, ?) and `posts`.`status` = ?) and (`posts`.`page` = ?)) order by CASE WHEN posts.status = \'scheduled\' THEN 1 WHEN posts.status = \'draft\' THEN 2 ELSE 3 END ASC,CASE WHEN posts.status != \'draft\' THEN posts.published_at END DESC,posts.updated_at DESC,posts.id DESC'); queries[1].bindings.should.eql([ 'published', 'draft', 'published', - 'post' + false ]); }); }); @@ -322,7 +322,7 @@ describe('Unit: models/post', function () { should(filter).equal(null); }); - it('returns type:post filter for public context', function () { + it('returns page:false filter for public context', function () { const options = { context: { public: true @@ -331,17 +331,17 @@ describe('Unit: models/post', function () { const filter = defaultFilters({}, options); - filter.should.equal('type:post'); + filter.should.equal('page:false'); }); - it('returns type:post+status:published filter for non public context', function () { + it('returns page:false+status:published filter for non public context', function () { const options = { context: 'user' }; const filter = defaultFilters({}, options); - filter.should.equal('type:post+status:published'); + filter.should.equal('page:false+status:published'); }); }); }); diff --git a/core/test/unit/services/rss/generate-feed_spec.js b/core/test/unit/services/rss/generate-feed_spec.js index 492a52d51c..5dd71761ba 100644 --- a/core/test/unit/services/rss/generate-feed_spec.js +++ b/core/test/unit/services/rss/generate-feed_spec.js @@ -16,7 +16,7 @@ describe('RSS: Generate Feed', function () { posts = _.cloneDeep(testUtils.DataGenerator.forKnex.posts); posts = _.filter(posts, function filter(post) { - return post.status === 'published' && post.type === 'post'; + return post.status === 'published' && post.page === false; }); _.each(posts, function (post) { diff --git a/core/test/unit/services/slack_spec.js b/core/test/unit/services/slack_spec.js index 5db5dd859a..45f10482de 100644 --- a/core/test/unit/services/slack_spec.js +++ b/core/test/unit/services/slack_spec.js @@ -192,7 +192,7 @@ describe('Slack', function () { }); it('does not make a request if post is a page', function () { - const post = testUtils.DataGenerator.forKnex.createPost({type: 'page'}); + const post = testUtils.DataGenerator.forKnex.createPost({page: true}); isPostStub.returns(true); settingsCacheStub.withArgs('slack').returns(slackObjWithUrl); diff --git a/core/test/utils/fixtures/data-generator.js b/core/test/utils/fixtures/data-generator.js index c33e9dd1ab..a7776848da 100644 --- a/core/test/utils/fixtures/data-generator.js +++ b/core/test/utils/fixtures/data-generator.js @@ -74,14 +74,14 @@ DataGenerator.Content = { 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.

'), - type: 'page' + page: true }, { 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.

'), - type: 'page', + page: true, status: 'draft' }, { @@ -468,7 +468,7 @@ DataGenerator.forKnex = (function () { status: 'published', feature_image: null, featured: false, - type: 'post', + page: false, slug: 'slug', author_id: DataGenerator.Content.users[0].id, updated_at: new Date(),