diff --git a/apps/admin/src/routes.tsx b/apps/admin/src/routes.tsx index d92719dd621..38c398414b6 100644 --- a/apps/admin/src/routes.tsx +++ b/apps/admin/src/routes.tsx @@ -36,7 +36,6 @@ const EMBER_ROUTES: string[] = [ "/posts/analytics/:postId/debug", "/restore", "/editor/*", - "/explore/*", "/migrate/*", "/members-activity", ]; diff --git a/apps/ember-admin/app/components/gh-explore-iframe.hbs b/apps/ember-admin/app/components/gh-explore-iframe.hbs deleted file mode 100644 index 25f3309c64b..00000000000 --- a/apps/ember-admin/app/components/gh-explore-iframe.hbs +++ /dev/null @@ -1,9 +0,0 @@ - \ No newline at end of file diff --git a/apps/ember-admin/app/components/gh-explore-iframe.js b/apps/ember-admin/app/components/gh-explore-iframe.js deleted file mode 100644 index eec811159a7..00000000000 --- a/apps/ember-admin/app/components/gh-explore-iframe.js +++ /dev/null @@ -1,77 +0,0 @@ -import Component from '@glimmer/component'; -import {action} from '@ember/object'; -import {inject as service} from '@ember/service'; - -export default class GhExploreIframe extends Component { - @service explore; - @service router; - @service feature; - - constructor() { - super(...arguments); - window.addEventListener('message', this.handleIframeMessage); - } - - willDestroy() { - super.willDestroy(...arguments); - window.removeEventListener('message', this.handleIframeMessage); - } - - @action - setup() { - // Only begin setup when Explore window is toggled open - // to avoid unnecessary loading of assets - if (this.explore.exploreWindowOpen) { - this.explore.getExploreIframe().src = this.explore.iframeURL; - } - } - - @action - async handleIframeMessage(event) { - if (this.isDestroyed || this.isDestroying) { - return; - } - - // only process messages coming from the explore iframe - if (event?.data && this.explore.iframeURL.includes(event?.origin)) { - if (event.data?.request === 'apiUrl') { - this._handleUrlRequest(); - } - - if (event.data?.route) { - this._handleRouteUpdate(event.data); - } - - if (event.data?.siteData) { - this._handleSiteDataUpdate(event.data); - } - } - } - - @action - async handleDarkModeChange() { - if (this.explore.exploreWindowOpen) { - this.explore.sendUIUpdate({darkMode: this.feature.nightShift}); - } - } - - // The iframe can send route updates to navigate to within Admin, as some routes - // have to be rendered within the iframe and others require to break out of it. - _handleRouteUpdate(data) { - const route = data.route; - this.explore.isIframeTransition = route?.includes('/explore'); - this.explore.toggleExploreWindow(this.explore.isIframeTransition); - this.router.transitionTo(route); - } - - _handleUrlRequest() { - this.explore.getExploreIframe().contentWindow.postMessage({ - request: 'apiUrl', - response: {apiUrl: this.explore.apiUrl, darkMode: this.feature.nightShift} - }, '*'); - } - - _handleSiteDataUpdate(data) { - this.explore.siteData = data?.siteData ?? {}; - } -} diff --git a/apps/ember-admin/app/components/gh-explore-modal.hbs b/apps/ember-admin/app/components/gh-explore-modal.hbs deleted file mode 100644 index e05dcbcfde9..00000000000 --- a/apps/ember-admin/app/components/gh-explore-modal.hbs +++ /dev/null @@ -1,5 +0,0 @@ -
-
- -
-
\ No newline at end of file diff --git a/apps/ember-admin/app/components/gh-explore-modal.js b/apps/ember-admin/app/components/gh-explore-modal.js deleted file mode 100644 index 90b5bf04b6d..00000000000 --- a/apps/ember-admin/app/components/gh-explore-modal.js +++ /dev/null @@ -1,10 +0,0 @@ -import Component from '@glimmer/component'; -import {inject as service} from '@ember/service'; - -export default class GhExploreModal extends Component { - @service explore; - - get visibilityClass() { - return this.explore.exploreWindowOpen ? 'gh-explore' : 'gh-explore closed'; - } -} diff --git a/apps/ember-admin/app/controllers/application.js b/apps/ember-admin/app/controllers/application.js index 820b40f2a47..212cb4c908e 100644 --- a/apps/ember-admin/app/controllers/application.js +++ b/apps/ember-admin/app/controllers/application.js @@ -6,7 +6,6 @@ import {inject as service} from '@ember/service'; export default class ApplicationController extends Controller { @service billing; - @service explore; @service router; @service session; @service settings; diff --git a/apps/ember-admin/app/controllers/explore.js b/apps/ember-admin/app/controllers/explore.js deleted file mode 100644 index 207b0e134d5..00000000000 --- a/apps/ember-admin/app/controllers/explore.js +++ /dev/null @@ -1,58 +0,0 @@ -import Controller from '@ember/controller'; -import {action} from '@ember/object'; -import {inject} from 'ghost-admin/decorators/inject'; -import {inject as service} from '@ember/service'; - -export default class ExploreController extends Controller { - @inject config; - @service explore; - @service router; - - get exploreCredentials() { - const explore = this.model.findBy('slug', 'ghost-explore'); - const adminKey = explore.adminKey; - - return adminKey.secret; - } - - get visibilityClass() { - return this.explore.isIframeTransition ? 'explore iframe-explore-container' : ' explore fullscreen-explore-container'; - } - - @action - closeConnect() { - if (this.explore.isIframeTransition) { - this.explore.sendRouteUpdate({path: '/explore'}); - this.router.transitionTo('/explore'); - } else { - this.router.transitionTo('/analytics'); - } - } - - @action - submitExploreSite() { - const token = this.exploreCredentials; - const apiUrl = this.explore.apiUrl; - - const query = new URLSearchParams(); - - query.append('token', token); - query.append('url', apiUrl); - - if (this.explore.isIframeTransition) { - this.explore.sendRouteUpdate({path: this.explore.submitRoute, queryParams: query.toString()}); - - // Set a short timeout to give Explore enough time to navigate - // to the submit page and fetch the required site data - setTimeout(() => { - this.explore.toggleExploreWindow(true); - }, 500); - } else { - // Ghost Explore URL to submit a new site - const destination = new URL(`${this.explore.exploreUrl}${this.explore.submitRoute}`); - destination.search = query; - - window.location = destination.toString(); - } - } -} diff --git a/apps/ember-admin/app/router.js b/apps/ember-admin/app/router.js index 25ff8234910..8db9966b0d2 100644 --- a/apps/ember-admin/app/router.js +++ b/apps/ember-admin/app/router.js @@ -37,18 +37,6 @@ Router.map(function () { this.route('tag.new', {path: '/tags/new'}); this.route('tag', {path: '/tags/:tag_slug'}); - this.route('explore', function () { - // actual Ember route, not rendered in iframe - this.route('connect'); - // iframe sub pages, used for categories - this.route('explore-sub', {path: '/*sub'}, function () { - // needed to allow search to work, as it uses URL - // params for search queries. They don't need to - // be visible, but may not be cut off. - this.route('explore-query', {path: '/*query'}); - }); - }); - this.route('migrate', function () { this.route('migrate', {path: '/*platform'}); }); diff --git a/apps/ember-admin/app/routes/explore.js b/apps/ember-admin/app/routes/explore.js deleted file mode 100644 index 0b34b48e762..00000000000 --- a/apps/ember-admin/app/routes/explore.js +++ /dev/null @@ -1,10 +0,0 @@ -import AuthenticatedRoute from 'ghost-admin/routes/authenticated'; -import {inject as service} from '@ember/service'; - -export default class ExploreRoute extends AuthenticatedRoute { - @service store; - - model() { - return this.store.findAll('integration'); - } -} diff --git a/apps/ember-admin/app/routes/explore/connect.js b/apps/ember-admin/app/routes/explore/connect.js deleted file mode 100644 index 6ab296e8b3d..00000000000 --- a/apps/ember-admin/app/routes/explore/connect.js +++ /dev/null @@ -1,10 +0,0 @@ -import ExploreRoute from './index'; - -export default class ExploreConnectRoute extends ExploreRoute { - controllerName = 'explore'; - - // Ensure to always close the iframe, as we're now on an Ember route - beforeModel() { - this.explore.toggleExploreWindow(false); - } -} diff --git a/apps/ember-admin/app/routes/explore/explore-sub.js b/apps/ember-admin/app/routes/explore/explore-sub.js deleted file mode 100644 index f504100aabe..00000000000 --- a/apps/ember-admin/app/routes/explore/explore-sub.js +++ /dev/null @@ -1,5 +0,0 @@ -import ExploreRoute from './index'; - -export default class ExploreSubRoute extends ExploreRoute { - controllerName = 'explore'; -} diff --git a/apps/ember-admin/app/routes/explore/index.js b/apps/ember-admin/app/routes/explore/index.js deleted file mode 100644 index 43a8e43135e..00000000000 --- a/apps/ember-admin/app/routes/explore/index.js +++ /dev/null @@ -1,80 +0,0 @@ -import AuthenticatedRoute from 'ghost-admin/routes/authenticated'; -import {action} from '@ember/object'; -import {inject as service} from '@ember/service'; - -export default class ExploreIndexRoute extends AuthenticatedRoute { - @service explore; - @service store; - @service router; - @service feature; - - beforeModel(transition) { - super.beforeModel(...arguments); - - // Usage of query param to ensure that sites can be submitted across - // older versions of Ghost where the `connect` part lives in the - // explore route directly. By using the query param, we avoid causing - // a 404 and handle the redirect here. - if (transition.to?.queryParams?.new === 'true') { - this.explore.isIframeTransition = false; - return this.router.transitionTo('explore.connect'); - } - - // Ensure the explore window is set to open - if (transition.to?.localName === 'index') { - this.explore.isIframeTransition = true; - this.explore.openExploreWindow(); - } - } - - model() { - return this.store.findAll('integration'); - } - - @action - willTransition(transition) { - let isExploreTransition = false; - - if (transition) { - let destinationUrl = (typeof transition.to === 'string') - ? transition.to - : (transition.intent - ? transition.intent.url - : ''); - - if (destinationUrl?.includes('/explore')) { - isExploreTransition = true; - this.explore.isIframeTransition = isExploreTransition; - - if (destinationUrl?.includes('/explore/submit')) { - // only show the submit page if the site is already submitted - // and redirect to the connect page if not. - if (Object.keys(this?.explore?.siteData).length >= 1) { - this.controllerFor('explore').submitExploreSite(); - } else { - transition.abort(); - return this.router.transitionTo('explore.connect'); - } - } else { - let path = destinationUrl.replace(/explore\//, ''); - path = path === '/' ? '/explore/' : path; - - if (destinationUrl?.includes('/explore/about')) { - window.open(`${this.explore.exploreUrl}about/`, '_blank').focus(); - path = '/explore/'; - } - // Send the updated route to the iframe - this.explore.sendRouteUpdate({path}); - } - } - } - - this.explore.toggleExploreWindow(isExploreTransition); - } - - buildRouteInfoMetadata() { - return { - titleToken: 'Explore' - }; - } -} diff --git a/apps/ember-admin/app/services/explore.js b/apps/ember-admin/app/services/explore.js deleted file mode 100644 index 5efaa2b4628..00000000000 --- a/apps/ember-admin/app/services/explore.js +++ /dev/null @@ -1,136 +0,0 @@ -import Service, {inject as service} from '@ember/service'; -import {inject} from 'ghost-admin/decorators/inject'; -import {tracked} from '@glimmer/tracking'; - -export default class ExploreService extends Service { - @service router; - @service feature; - @service ghostPaths; - - @inject config; - - exploreUrl = 'https://ghost.org/explore/'; - exploreRouteRoot = '#/explore'; - submitRoute = 'submit'; - - @tracked exploreWindowOpen = false; - @tracked siteData = null; - @tracked isIframeTransition = false; - - get apiUrl() { - const origin = new URL(window.location.origin); - const subdir = this.ghostPaths.subdir; - // We want the API URL without protocol - let url = this.ghostPaths.url.join(origin.host, subdir); - - return url.replace(/\/$/, ''); - } - - get iframeURL() { - let url = this.exploreUrl; - - if (window.location.hash && window.location.hash.includes(this.exploreRouteRoot)) { - let destinationRoute = window.location.hash.replace(this.exploreRouteRoot, ''); - - // Connect is an Ember route, do not use it as iframe src - if (destinationRoute && !destinationRoute.includes('connect')) { - url += destinationRoute.replace(/^\//, ''); - } - } - - return url; - } - - constructor() { - super(...arguments); - - if (this.exploreUrl) { - window.addEventListener('message', (event) => { - if (event && event.data && event.data.route) { - this.handleRouteChangeInIframe(event.data.route); - } - }); - } - } - - handleRouteChangeInIframe(destinationRoute) { - if (this.exploreWindowOpen) { - let exploreRoute = this.exploreRouteRoot; - - if (destinationRoute.match(/^\/explore(\/.*)?/)) { - destinationRoute = destinationRoute.replace(/\/explore/, ''); - } - - if (destinationRoute !== '/') { - exploreRoute += destinationRoute; - } - - if (window.location.hash !== exploreRoute) { - window.history.replaceState(window.history.state, '', exploreRoute); - } - } - } - - // Sends a route update to a child route in the BMA, because we can't control - // navigating to it otherwise - sendRouteUpdate(route) { - this.getExploreIframe().contentWindow.postMessage({ - query: 'routeUpdate', - response: route - }, '*'); - } - - sendUIUpdate(data) { - this.getExploreIframe().contentWindow.postMessage({ - query: 'uiUpdate', - response: data - }, '*'); - } - - // Controls explore window modal visibility and sync of the URL visible in browser - // and the URL opened on the iframe. It is responsible to non user triggered iframe opening, - // for example: by entering "/explore" route in the URL or using history navigation (back and forward) - toggleExploreWindow(value) { - if (this.config.hostSettings?.forceUpgrade && value) { - // don't attempt to open Explore iframe when in Force Upgrade state - return; - } - - if (this.exploreWindowOpen && value) { - // don't attempt to open again - return; - } - this.exploreWindowOpen = value; - } - - openExploreWindow() { - if (this.config.hostSettings?.forceUpgrade) { - // don't attempt to open Explore iframe when in Force Upgrade state - return; - } - if (this.exploreWindowOpen) { - // don't attempt to open again - return; - } - - // Begin loading the iframe and setting the src if it's not already set - this.ensureIframeIsLoaded(); - - // Ensures correct iframe URL calculation when syncing iframe location - // in toggleExploreWindow - window.location.hash = '/explore'; - - this.router.transitionTo('/explore'); - this.toggleExploreWindow(true); - } - - ensureIframeIsLoaded() { - if (this.getExploreIframe() && !this.getExploreIframe()?.src) { - this.getExploreIframe().src = this.iframeURL; - } - } - - getExploreIframe() { - return document.getElementById('explore-frame'); - } -} diff --git a/apps/ember-admin/app/styles/app-dark.css b/apps/ember-admin/app/styles/app-dark.css index 06548b309b2..503554e86ad 100644 --- a/apps/ember-admin/app/styles/app-dark.css +++ b/apps/ember-admin/app/styles/app-dark.css @@ -60,7 +60,6 @@ @import "layouts/post-history.css"; @import "layouts/post-preview.css"; @import "layouts/tiers.css"; -@import "layouts/explore.css"; :root { /* Primary colours */ @@ -1058,24 +1057,6 @@ kbd { fill: var(--midgrey-d2); } -/* Explore */ - -.fullscreen-explore-container { - background: var(--main-bg-color); -} - -.explore-api { - color: var(--middarkgrey); -} - -.explore-permissions p { - color: #fff; -} - -.explore-permissions { - background: var(--whitegrey-l1); -} - /* Settings Links */ .kg-settings-link-url::before { diff --git a/apps/ember-admin/app/styles/app.css b/apps/ember-admin/app/styles/app.css index 4dab315b47f..597b9218e85 100644 --- a/apps/ember-admin/app/styles/app.css +++ b/apps/ember-admin/app/styles/app.css @@ -63,7 +63,6 @@ @import "layouts/post-history.css"; @import "layouts/post-preview.css"; @import "layouts/tiers.css"; -@import "layouts/explore.css"; @import "layouts/mentions.css"; @import "layouts/migrate.css"; diff --git a/apps/ember-admin/app/styles/layouts/explore.css b/apps/ember-admin/app/styles/layouts/explore.css deleted file mode 100644 index f016b981a95..00000000000 --- a/apps/ember-admin/app/styles/layouts/explore.css +++ /dev/null @@ -1,242 +0,0 @@ -.gh-explore { - position: absolute; - top: 0; - left: 0; - height: 100%; - width: 100%; - z-index: 9999; - background: var(--main-bg-color); -} - -.gh-explore-container { - position: relative; - height: 100%; - width: 100%; -} - -.gh-explore.closed { - display: none; -} - -.gh-explore .close { - position: absolute; - top: 19px; - right: 19px; - z-index: 9999; - margin: 0; - padding: 0; - width: 16px; - height: 16px; - border: none; -} - -.gh-explore .explore-frame { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - border: none; - transform: translate3d(0, 0, 0); -} - -.gh-explore-close { - width: calc(50vw - 200px) -} - -.gh-explore-close button { - stroke: var(--midgrey); - opacity: 0.6; - transition: all 0.2s ease-in-out; - top: 25px; -} - -/* Connect */ -.explore { - position: relative; -} - -.iframe-explore-container { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - border: none; - transform: translate3d(0, 0, 0); - height: 100vh; - background: linear-gradient(180deg, var(--white) 0%, #E1E1E1 100%); -} - -.fullscreen-explore-container { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 10000; - height: 100vh; - background: linear-gradient(180deg, var(--white) 0%, #E1E1E1 100%); - overflow: hidden; -} - -.explore-close { - display: flex; - justify-content: flex-end; - align-items: center; - padding: 2rem; -} - -.explore-close a { - color: var(--middarkgrey); -} - -.explore-close a svg { - stroke: var(--middarkgrey); - width: 18px; - height: auto; -} - -.explore-close a svg path { - stroke-width: 1px; -} - -.explore-close a:hover svg { - stroke: var(--darkgrey); -} - -.explore-content { - display: flex; - align-items: center; - flex-direction: column; - min-height: 100%; - padding: 8vmin 2vmin 4vmin; -} - -.explore-header { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; -} - -.explore-header h1 { - margin: 1.6rem 0 0; - font-size: 3.6rem; - text-align: center; - font-weight: 700; - letter-spacing: -.03em; - line-height: 40px; -} - -.explore-api { - margin-bottom: 50px; - color: rgba(0, 0, 0, 0.52); - font-size: 2.3rem; - font-weight: 400; - line-height: 30px; - text-align: center; - letter-spacing: -0.03em; -} - -.explore-permissions { - background: var(--white); - padding: 3rem 3.5rem; - max-width: 457px; - width: 100%; - border-radius: 6px; -} - -.explore-permissions svg path { - stroke: #86C600; -} - -.explore-permissions > div { - display: flex; - flex-direction: row; - align-items: baseline; -} - -.explore-permissions p { - color: rgba(0, 0, 0, 0.66); - margin: 0; - font-size: 1.9rem; - font-weight: 400; - letter-spacing: -0.03em; - line-height: 1.32; -} - -.explore-permissions div:not(:last-of-type) { - margin-bottom: 3rem; -} - -.explore-permissions > div span { - padding-right: 18px; -} - -.explore button { - margin-top: 4vmin; - max-width: 457px; - width: 100%; - height: 50px; - border-radius: 6px; -} - -.explore button span { - font-size: 1.7rem; - font-weight: 500; - color: var(--white); -} - -.explore button svg { - fill: var(--white); - margin-left: 0.1em; - height: 14px; -} - -@media (max-width: 800px) { - .explore-content { - padding: 2vmin; - } - .explore-header { - margin-top: 10vmin; - } - .explore-header svg { - width: 7rem; - } - .explore-header h1 { - font-size: 2.8rem; - } - .explore-api { - font-size: 1.8rem; - } - .explore-permissions { - padding: 2rem 2.5rem; - } - .explore-permissions p { - font-size: 1.6rem; - } -} - -@media (max-width: 500px) { - .explore-header h1 { - font-size: 2.4rem; - } - .explore-api { - font-size: 1.6rem; - margin-bottom: 20px; - } - .explore-permissions div:not(:last-of-type) { - margin-bottom: 2rem; - } - .explore-permissions > div span { - padding-right: 12px; - } - .explore-permissions > div span svg { - width: 1.8rem; - } -} diff --git a/apps/ember-admin/app/templates/application.hbs b/apps/ember-admin/app/templates/application.hbs index aead6868001..0d474701667 100644 --- a/apps/ember-admin/app/templates/application.hbs +++ b/apps/ember-admin/app/templates/application.hbs @@ -33,8 +33,6 @@ {{#if this.showBilling}} {{/if}} - - diff --git a/apps/ember-admin/app/templates/explore/connect.hbs b/apps/ember-admin/app/templates/explore/connect.hbs deleted file mode 100644 index b7574489522..00000000000 --- a/apps/ember-admin/app/templates/explore/connect.hbs +++ /dev/null @@ -1,34 +0,0 @@ -
- -
- -
- {{svg-jar "ghost-orb-pink" alt="Ghost" class="w25 v-mid"}} -

Connect to Ghost Explore.

-

{{this.apiUrl}}

-
- -
-
- {{svg-jar "check-circle" class="w6 v-mid" alt="checkmark"}} -

Allow read-only access to your site data to create a directory listing.

-
-
- {{svg-jar "check-circle" class="w6 v-mid" alt="checkmark"}} -

You’ll be able to choose what data is shown publicly or hidden.

-
-
- {{svg-jar "check-circle" class="w6 v-mid" alt="checkmark"}} -

Your site will be promoted across the entire Ghost ecosystem.

-
-
- - -
-
\ No newline at end of file diff --git a/apps/ember-admin/tests/unit/routes/explore-test.js b/apps/ember-admin/tests/unit/routes/explore-test.js deleted file mode 100644 index 9f1a37b9299..00000000000 --- a/apps/ember-admin/tests/unit/routes/explore-test.js +++ /dev/null @@ -1,12 +0,0 @@ -import {describe, it} from 'mocha'; -import {expect} from 'chai'; -import {setupTest} from 'ember-mocha'; - -describe('Unit | Route | explore', function () { - setupTest(); - - it('exists', function () { - let route = this.owner.lookup('route:explore.connect'); - expect(route).to.be.ok; - }); -}); diff --git a/ghost/core/core/server/api/endpoints/explore.js b/ghost/core/core/server/api/endpoints/explore.js deleted file mode 100644 index aa9c380c4d7..00000000000 --- a/ghost/core/core/server/api/endpoints/explore.js +++ /dev/null @@ -1,18 +0,0 @@ -const exploreService = require('../../services/explore'); - -/** @type {import('@tryghost/api-framework').Controller} */ -const controller = { - docName: 'explore', - - read: { - headers: { - cacheInvalidate: false - }, - permissions: true, - query() { - return exploreService.fetchData(); - } - } -}; - -module.exports = controller; diff --git a/ghost/core/core/server/api/endpoints/index.js b/ghost/core/core/server/api/endpoints/index.js index 48d63adff79..7d4eacdc51a 100644 --- a/ghost/core/core/server/api/endpoints/index.js +++ b/ghost/core/core/server/api/endpoints/index.js @@ -172,10 +172,6 @@ module.exports = { return apiFramework.pipeline(require('./config'), localUtils); }, - get explore() { - return apiFramework.pipeline(require('./explore'), localUtils); - }, - get themes() { return apiFramework.pipeline(require('./themes'), localUtils); }, diff --git a/ghost/core/core/server/api/endpoints/utils/serializers/output/explore.js b/ghost/core/core/server/api/endpoints/utils/serializers/output/explore.js deleted file mode 100644 index edd52646673..00000000000 --- a/ghost/core/core/server/api/endpoints/utils/serializers/output/explore.js +++ /dev/null @@ -1,11 +0,0 @@ -const debug = require('@tryghost/debug')('api:endpoints:utils:serializers:output:explore'); - -module.exports = { - all(data, apiConfig, frame) { - debug('all'); - - frame.response = { - explore: data - }; - } -}; diff --git a/ghost/core/core/server/api/endpoints/utils/serializers/output/index.js b/ghost/core/core/server/api/endpoints/utils/serializers/output/index.js index ea3ad909c91..919a7ff9740 100644 --- a/ghost/core/core/server/api/endpoints/utils/serializers/output/index.js +++ b/ghost/core/core/server/api/endpoints/utils/serializers/output/index.js @@ -28,10 +28,6 @@ module.exports = { return require('./db'); }, - get explore() { - return require('./explore'); - }, - get pages() { return require('./pages'); }, diff --git a/ghost/core/core/server/data/migrations/versions/6.58/2026-08-11-10-34-25-remove-legacy-explore-integration.js b/ghost/core/core/server/data/migrations/versions/6.58/2026-08-11-10-34-25-remove-legacy-explore-integration.js new file mode 100644 index 00000000000..b71a35fdb5b --- /dev/null +++ b/ghost/core/core/server/data/migrations/versions/6.58/2026-08-11-10-34-25-remove-legacy-explore-integration.js @@ -0,0 +1,131 @@ +const logging = require('@tryghost/logging'); +const ObjectID = require('bson-objectid').default; +const { + createTransactionalMigration, + combineTransactionalMigrations, + createRemovePermissionMigration +} = require('../../utils'); + +const INTEGRATION = { + type: 'core', + name: 'Ghost Explore', + slug: 'ghost-explore', + description: 'Built-in Ghost Explore integration' +}; + +const ROLE = { + name: 'Ghost Explore Integration', + description: 'Internal Integration for the Ghost Explore directory' +}; + +const PERMISSION = { + name: 'Read explore data', + action: 'read', + object: 'explore' +}; + +// Mirrors the roles the permission was originally granted to in 5.3 +const PERMISSION_ROLES = ['Administrator', 'Admin Integration', ROLE.name]; + +// `createRemovePermissionMigration` only unlinks the permission from roles, so any direct +// user grant would be left pointing at a deleted permission. Ghost only ever grants this +// via roles, so there is nothing to restore on the way back down. +const removeDirectUserGrants = createTransactionalMigration( + async function up(knex) { + const permission = await knex('permissions').where({ + name: PERMISSION.name, + action_type: PERMISSION.action, + object_type: PERMISSION.object + }).first(); + + if (!permission) { + logging.warn(`Skipping cleanup of direct "${PERMISSION.name}" user grants - the permission does not exist`); + return; + } + + const removed = await knex('permissions_users').where('permission_id', permission.id).del(); + logging.info(`Removed ${removed} direct "${PERMISSION.name}" user grants`); + }, + async function down() { + logging.info(`Skipping restore of direct "${PERMISSION.name}" user grants - Ghost only grants it via roles`); + } +); + +const removeIntegration = createTransactionalMigration( + async function up(knex) { + const integration = await knex('integrations').select('id').where('slug', INTEGRATION.slug).first(); + + if (!integration) { + logging.warn(`Skipping removal of ${INTEGRATION.slug} integration - it does not exist`); + return; + } + + await knex('api_keys').where('integration_id', integration.id).del(); + await knex('integrations').where('id', integration.id).del(); + logging.info(`Removed ${INTEGRATION.slug} integration and API keys`); + }, + async function down(knex) { + const existing = await knex('integrations').select('id').where('slug', INTEGRATION.slug).first(); + + if (existing) { + logging.warn(`Skipping restore of ${INTEGRATION.slug} integration - it already exists`); + return; + } + + await knex('integrations').insert({ + id: new ObjectID().toHexString(), + ...INTEGRATION, + created_at: knex.raw('CURRENT_TIMESTAMP'), + updated_at: knex.raw('CURRENT_TIMESTAMP') + }); + + // Deliberately no API key: the original secret is unrecoverable, and a rollback is + // about getting the schema back to what the previous version expects rather than + // reconnecting Explore - ghost.org stopped calling this endpoint long ago. Anyone + // who needs Admin API access should create a custom integration instead. + logging.info(`Restored ${INTEGRATION.slug} integration without an API key`); + } +); + +const removeRole = createTransactionalMigration( + async function up(knex) { + const role = await knex('roles').select('id').where('name', ROLE.name).first(); + + if (!role) { + logging.warn(`Skipping removal of ${ROLE.name} role - it does not exist`); + return; + } + + await knex('api_keys').where('role_id', role.id).del(); + await knex('permissions_roles').where('role_id', role.id).del(); + await knex('roles_users').where('role_id', role.id).del(); + await knex('roles').where('id', role.id).del(); + logging.info(`Removed ${ROLE.name} role`); + }, + async function down(knex) { + const existing = await knex('roles').select('id').where('name', ROLE.name).first(); + + if (existing) { + logging.warn(`Skipping restore of ${ROLE.name} role - it already exists`); + return; + } + + await knex('roles').insert({ + id: new ObjectID().toHexString(), + ...ROLE, + created_at: knex.raw('CURRENT_TIMESTAMP'), + updated_at: knex.raw('CURRENT_TIMESTAMP') + }); + + logging.info(`Restored ${ROLE.name} role`); + } +); + +// Down migrations run in reverse, so the role is restored before the permission that has +// to be linked back to it +module.exports = combineTransactionalMigrations( + removeDirectUserGrants, + createRemovePermissionMigration(PERMISSION, PERMISSION_ROLES), + removeIntegration, + removeRole +); diff --git a/ghost/core/core/server/data/schema/fixtures/fixtures.json b/ghost/core/core/server/data/schema/fixtures/fixtures.json index c6613c1e35c..763be09d78a 100644 --- a/ghost/core/core/server/data/schema/fixtures/fixtures.json +++ b/ghost/core/core/server/data/schema/fixtures/fixtures.json @@ -99,10 +99,6 @@ "name": "Admin Integration", "description": "External Apps" }, - { - "name": "Ghost Explore Integration", - "description": "Internal Integration for the Ghost Explore directory" - }, { "name": "Self-Serve Migration Integration", "description": "Internal Integration for the Self-Serve migration tool" @@ -124,11 +120,6 @@ { "name": "Permission", "entries": [ - { - "name": "Read explore data", - "action_type": "read", - "object_type": "explore" - }, { "name": "Export database", "action_type": "exportContent", @@ -900,13 +891,6 @@ "type": "builtin", "api_keys": [{"type": "admin"}] }, - { - "slug": "ghost-explore", - "name": "Ghost Explore", - "description": "Built-in Ghost Explore integration", - "type": "core", - "api_keys": [{"type": "admin", "role": "Ghost Explore Integration"}] - }, { "slug": "self-serve-migration", "name": "Self-Serve Migration Integration", @@ -1002,7 +986,6 @@ "authentication": "reset", "members_stripe_connect": "auth", "newsletter": "all", - "explore": "read", "comment": "all", "link": "all", "mention": "browse", @@ -1021,9 +1004,6 @@ "automation": "poll", "gift": "flushReminders" }, - "Ghost Explore Integration": { - "explore": "read" - }, "Self-Serve Migration Integration": { "db": "importContent", "content_import": "importContent", @@ -1055,7 +1035,6 @@ "product": ["browse", "read", "add", "edit"], "offer": ["browse", "read", "add", "edit"], "newsletter": ["browse", "read", "add", "edit"], - "explore": "read", "comment": "all", "link": "all", "mention": "browse", diff --git a/ghost/core/core/server/services/explore/explore-service.js b/ghost/core/core/server/services/explore/explore-service.js deleted file mode 100644 index fc7c1d19277..00000000000 --- a/ghost/core/core/server/services/explore/explore-service.js +++ /dev/null @@ -1,59 +0,0 @@ -const ghostVersion = require('@tryghost/version'); - -module.exports = class ExploreService { - /** - * @param {Object} options - * @param {Object} options.MembersService - * @param {Object} options.PostsService - * @param {Object} options.PublicConfigService - * @param {Object} options.StatsService - * @param {Object} options.StripeService - * @param {Object} options.UserModel - */ - constructor({MembersService, PostsService, PublicConfigService, StatsService, StripeService, UserModel}) { - this.MembersService = MembersService; - this.PostsService = PostsService; - this.PublicConfigService = PublicConfigService; - this.StatsService = StatsService; - this.StripeService = StripeService; - this.UserModel = UserModel; - } - - /** - * Build and return the response object containing the data for the Ghost Explore endpoint - */ - async fetchData() { - const totalMembers = await this.MembersService.stats.getTotalMembers(); - const mrrStats = await this.StatsService.api.getMRRHistory(); - - const {description, icon, title, url, accent_color: accentColor, locale} = this.PublicConfigService.site; - - const exploreProperties = { - version: ghostVersion.full, - total_members: totalMembers, - mrr_stats: mrrStats, - site: { - description, - icon, - title, - url, - accent_color: accentColor, - locale - }, - stripe: { - configured: this.StripeService.api.configured, - livemode: (this.StripeService.api.configured && this.StripeService.api.mode === 'live') - } - }; - - const mostRecentlyPublishedPost = await this.PostsService.stats.getMostRecentlyPublishedPostDate(); - const totalPostsPublished = await this.PostsService.stats.getTotalPostsPublished(); - exploreProperties.most_recently_published_at = mostRecentlyPublishedPost ?? null; - exploreProperties.total_posts_published = totalPostsPublished ?? null; - - const owner = await this.UserModel.findOne({role: 'Owner', status: 'all'}); - exploreProperties.owner_email = owner?.get('email') ?? null; - - return exploreProperties; - } -}; diff --git a/ghost/core/core/server/services/explore/index.js b/ghost/core/core/server/services/explore/index.js deleted file mode 100644 index 99bb608c1c5..00000000000 --- a/ghost/core/core/server/services/explore/index.js +++ /dev/null @@ -1,18 +0,0 @@ -const ExploreService = require('./explore-service'); - -const MembersService = require('../members'); -const PostsService = require('../posts/posts-service-instance')(); -const PublicConfigService = require('../public-config'); -const StatsService = require('../stats'); -const StripeService = require('../stripe'); - -const models = require('../../models'); - -module.exports = new ExploreService({ - MembersService, - PostsService, - PublicConfigService, - StatsService, - StripeService, - UserModel: models.User -}); diff --git a/ghost/core/core/server/web/api/endpoints/admin/middleware.js b/ghost/core/core/server/web/api/endpoints/admin/middleware.js index c6c45a15f7e..5479850743c 100644 --- a/ghost/core/core/server/web/api/endpoints/admin/middleware.js +++ b/ghost/core/core/server/web/api/endpoints/admin/middleware.js @@ -66,7 +66,6 @@ const tokenPermissionCheck = function tokenPermissionCheck(req, res, next) { newsletters: ['GET', 'PUT', 'POST'], automations: ['PUT'], config: ['GET'], - explore: ['GET'], schedules: ['PUT'], gifts: ['PUT'], files: ['POST'], diff --git a/ghost/core/core/server/web/api/endpoints/admin/routes.js b/ghost/core/core/server/web/api/endpoints/admin/routes.js index f24d414cbdd..81866b875ed 100644 --- a/ghost/core/core/server/web/api/endpoints/admin/routes.js +++ b/ghost/core/core/server/web/api/endpoints/admin/routes.js @@ -23,9 +23,6 @@ module.exports = function apiRoutes() { router.get('/config', mw.authAdminApi, http(api.config.read)); router.get('/config/featurebase', mw.authAdminApi, http(api.config.featurebase)); - // ## Ghost Explore - router.get('/explore', mw.authAdminApi, http(api.explore.read)); - // ## Posts router.get('/posts', mw.authAdminApi, http(api.posts.browse)); router.get('/posts/export', mw.authAdminApi, http(api.posts.exportCSV)); diff --git a/ghost/core/test/e2e-api/admin/__snapshots__/explore.test.js.snap b/ghost/core/test/e2e-api/admin/__snapshots__/explore.test.js.snap deleted file mode 100644 index 38f040d023a..00000000000 --- a/ghost/core/test/e2e-api/admin/__snapshots__/explore.test.js.snap +++ /dev/null @@ -1,60 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`Explore API Read Can request Explore data 1: [body] 1`] = ` -Object { - "explore": Object { - "most_recently_published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "mrr_stats": Object { - "data": Array [ - Object { - "currency": "usd", - "date": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}/, - "mrr": 0, - }, - Object { - "currency": "usd", - "date": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}/, - "mrr": 1000, - }, - ], - "meta": Object { - "totals": Array [ - Object { - "currency": "usd", - "mrr": 1000, - }, - ], - }, - }, - "owner_email": "jbloggs@example.com", - "site": Object { - "accent_color": "#FF1A75", - "description": "Thoughts, stories and ideas", - "icon": null, - "locale": "en", - "title": "Ghost", - "url": "http://127.0.0.1:2369/", - }, - "stripe": Object { - "configured": true, - "livemode": false, - }, - "total_members": 8, - "total_posts_published": Any, - "version": StringMatching /\\\\d\\+\\\\\\.\\\\d\\+\\\\\\.\\\\d\\+/, - }, -} -`; - -exports[`Explore API Read Can request Explore data 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "http://127.0.0.1:2369", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": StringMatching /\\\\d\\+/, - "content-type": "application/json; charset=utf-8", - "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Version, Origin, Accept-Encoding", - "x-powered-by": "Express", -} -`; diff --git a/ghost/core/test/e2e-api/admin/__snapshots__/roles.test.js.snap b/ghost/core/test/e2e-api/admin/__snapshots__/roles.test.js.snap index d4f0c516781..524a543f0f9 100644 --- a/ghost/core/test/e2e-api/admin/__snapshots__/roles.test.js.snap +++ b/ghost/core/test/e2e-api/admin/__snapshots__/roles.test.js.snap @@ -45,13 +45,6 @@ Object { "name": "Admin Integration", "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, }, - Object { - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "description": "Internal Integration for the Ghost Explore directory", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": "Ghost Explore Integration", - "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - }, Object { "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, "description": "Internal Integration for the Self-Serve migration tool", @@ -88,7 +81,7 @@ exports[`Roles API Can request all roles 2: [headers] 1`] = ` Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "1962", + "content-length": "1744", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, diff --git a/ghost/core/test/e2e-api/admin/explore.test.js b/ghost/core/test/e2e-api/admin/explore.test.js deleted file mode 100644 index 7b1b22363f3..00000000000 --- a/ghost/core/test/e2e-api/admin/explore.test.js +++ /dev/null @@ -1,41 +0,0 @@ -const {agentProvider, fixtureManager, matchers} = require('../../utils/e2e-framework'); -const {anyEtag, anyISODate, anyISODateTime, anyContentLength, anyContentVersion, stringMatching, anyNumber} = matchers; - -describe('Explore API', function () { - let agent; - - beforeAll(async function () { - agent = await agentProvider.getAdminAPIAgent(); - await fixtureManager.init('posts', 'members'); - await agent.loginAsOwner(); - }); - - describe('Read', function () { - it('Can request Explore data', async function () { - await agent - .get('explore/') - .expectStatus(200) - .matchBodySnapshot({ - explore: { - most_recently_published_at: anyISODateTime, - total_posts_published: anyNumber, - mrr_stats: { - data: [{ - date: anyISODate - }, - { - date: anyISODate - }] - }, - version: stringMatching(/\d+\.\d+\.\d+/) - } - }) - .matchHeaderSnapshot({ - etag: anyEtag, - // Special rule for this test, as the labs setting changes a lot - 'content-length': anyContentLength, - 'content-version': anyContentVersion - }); - }); - }); -}); diff --git a/ghost/core/test/e2e-api/admin/integrations.test.js b/ghost/core/test/e2e-api/admin/integrations.test.js index abf92fb9790..28bbe8c73b0 100644 --- a/ghost/core/test/e2e-api/admin/integrations.test.js +++ b/ghost/core/test/e2e-api/admin/integrations.test.js @@ -26,7 +26,7 @@ describe('Integrations API', function () { .expect('Cache-Control', testUtils.cacheRules.private) .expect(200); - assert.equal(res.body.integrations.length, 6); + assert.equal(res.body.integrations.length, 5); // there is no enforced order for integrations which makes order different on SQLite and MySQL const zapierIntegration = _.find(res.body.integrations, {name: 'Zapier'}); // from migrations diff --git a/ghost/core/test/e2e-api/admin/key-authentication.test.js b/ghost/core/test/e2e-api/admin/key-authentication.test.js index 4de97282b15..9e0a5f9e857 100644 --- a/ghost/core/test/e2e-api/admin/key-authentication.test.js +++ b/ghost/core/test/e2e-api/admin/key-authentication.test.js @@ -122,13 +122,13 @@ describe('Admin API key authentication', function () { sinon.assert.calledOnce(loggingStub); // CASE: Test with a different API key, related to a core integration - const secondResponse = await request.get(localUtils.API.getApiQuery('explore/')) + const secondResponse = await request.get(localUtils.API.getApiQuery('tags/')) .set('Authorization', `Ghost ${localUtils.getValidAdminToken('/admin/', 4)}`) .expect('Content-Type', /json/) .expect('Cache-Control', testUtils.cacheRules.private) .expect(200); - assertExists(secondResponse.body.explore); + assertExists(secondResponse.body.tags); }); }); }); diff --git a/ghost/core/test/e2e-api/admin/roles.test.js b/ghost/core/test/e2e-api/admin/roles.test.js index c11a972abb6..36a235d598a 100644 --- a/ghost/core/test/e2e-api/admin/roles.test.js +++ b/ghost/core/test/e2e-api/admin/roles.test.js @@ -21,7 +21,7 @@ describe('Roles API', function () { await agent.get('roles/') .expectStatus(200) .matchBodySnapshot({ - roles: Array(11).fill(rolesObjectMatcher) + roles: Array(10).fill(rolesObjectMatcher) }) .matchHeaderSnapshot({ 'content-version': anyContentVersion, diff --git a/ghost/core/test/integration/migrations/migration.test.js b/ghost/core/test/integration/migrations/migration.test.js index 391b0cc8c5c..e8d1298a095 100644 --- a/ghost/core/test/integration/migrations/migration.test.js +++ b/ghost/core/test/integration/migrations/migration.test.js @@ -91,7 +91,7 @@ describe('Migrations', function () { // Custom assertion to wrap all permissions function assertCompletePermissions(permissions) { // If you have to change this number, please add the relevant `assertHavePermission` checks below - assert.equal(permissions.length, 143); + assert.equal(permissions.length, 142); assertHavePermission(permissions, 'Export database', ['Administrator', 'DB Backup Integration']); assertHavePermission(permissions, 'Import database', ['Administrator', 'Self-Serve Migration Integration', 'DB Backup Integration']); @@ -225,8 +225,6 @@ describe('Migrations', function () { assertHavePermission(permissions, 'Edit newsletters', ['Administrator', 'Admin Integration']); assertHavePermission(permissions, 'Add newsletters', ['Administrator', 'Admin Integration']); - assertHavePermission(permissions, 'Read explore data', ['Administrator', 'Admin Integration', 'Ghost Explore Integration']); - assertHavePermission(permissions, 'Browse comments', ['Administrator', 'Admin Integration', 'Super Editor']); assertHavePermission(permissions, 'Read comments', ['Administrator', 'Admin Integration', 'Super Editor']); assertHavePermission(permissions, 'Edit comments', ['Administrator', 'Admin Integration', 'Super Editor']); @@ -303,18 +301,17 @@ describe('Migrations', function () { // Roles assert(roles); - assert.equal(roles.length, 11); + assert.equal(roles.length, 10); assert.equal(roles.at(0).get('name'), 'Administrator'); assert.equal(roles.at(1).get('name'), 'Editor'); assert.equal(roles.at(2).get('name'), 'Author'); assert.equal(roles.at(3).get('name'), 'Contributor'); assert.equal(roles.at(4).get('name'), 'Owner'); assert.equal(roles.at(5).get('name'), 'Admin Integration'); - assert.equal(roles.at(6).get('name'), 'Ghost Explore Integration'); - assert.equal(roles.at(7).get('name'), 'Self-Serve Migration Integration'); - assert.equal(roles.at(8).get('name'), 'DB Backup Integration'); - assert.equal(roles.at(9).get('name'), 'Scheduler Integration'); - assert.equal(roles.at(10).get('name'), 'Super Editor'); + assert.equal(roles.at(6).get('name'), 'Self-Serve Migration Integration'); + assert.equal(roles.at(7).get('name'), 'DB Backup Integration'); + assert.equal(roles.at(8).get('name'), 'Scheduler Integration'); + assert.equal(roles.at(9).get('name'), 'Super Editor'); // Permissions assertCompletePermissions(permissions.toJSON()); diff --git a/ghost/core/test/unit/server/data/schema/fixtures/fixture-manager.test.js b/ghost/core/test/unit/server/data/schema/fixtures/fixture-manager.test.js index 6bdc86bd262..48a31920fb1 100644 --- a/ghost/core/test/unit/server/data/schema/fixtures/fixture-manager.test.js +++ b/ghost/core/test/unit/server/data/schema/fixtures/fixture-manager.test.js @@ -398,7 +398,7 @@ describe('Migration Fixture Utils', function () { const rolesAllStub = sinon.stub(models.Role, 'findAll').returns(Promise.resolve(dataMethodStub)); const result = await fixtureManager.addFixturesForRelation(fixtures.relations[0]); - const FIXTURE_COUNT = 152; + const FIXTURE_COUNT = 149; assertExists(result); assert(_.isPlainObject(result)); assert.equal(result.expected, FIXTURE_COUNT); @@ -408,7 +408,7 @@ describe('Migration Fixture Utils', function () { sinon.assert.calledOnce(permsAllStub); sinon.assert.calledOnce(rolesAllStub); sinon.assert.callCount(dataMethodStub.filter, FIXTURE_COUNT); - sinon.assert.callCount(dataMethodStub.find, 10); + sinon.assert.callCount(dataMethodStub.find, 9); sinon.assert.callCount(baseUtilAttachStub, FIXTURE_COUNT); sinon.assert.callCount(fromItem.related, FIXTURE_COUNT); diff --git a/ghost/core/test/unit/server/data/schema/integrity.test.js b/ghost/core/test/unit/server/data/schema/integrity.test.js index db3f0917efe..58274817998 100644 --- a/ghost/core/test/unit/server/data/schema/integrity.test.js +++ b/ghost/core/test/unit/server/data/schema/integrity.test.js @@ -36,7 +36,7 @@ const parseYaml = require('../../../../../core/server/services/route-settings/ya describe('DB version integrity', function () { // Only these variables should need updating const currentSchemaHash = 'bb0b6f2ab291417ad87e0e7bc49c2310'; - const currentFixturesHash = '4e0c7b4fe3c1593e9d1fae1a891389ca'; + const currentFixturesHash = 'd4c9e4fabcec3365c42d37642216021b'; const currentSettingsHash = '8650db85b9a61afe4797ad6333066c62'; const currentRoutesHash = 'd8c25fa01bf6d22a2bcb05ba0de70dc1'; diff --git a/ghost/core/test/utils/fixtures/fixtures.json b/ghost/core/test/utils/fixtures/fixtures.json index f630c37c1d1..5515dda1618 100644 --- a/ghost/core/test/utils/fixtures/fixtures.json +++ b/ghost/core/test/utils/fixtures/fixtures.json @@ -100,10 +100,6 @@ "name": "Admin Integration", "description": "External Apps" }, - { - "name": "Ghost Explore Integration", - "description": "Internal Integration for the Ghost Explore directory" - }, { "name": "Self-Serve Migration Integration", "description": "Internal Integration for the Self-Serve migration tool" @@ -125,11 +121,6 @@ { "name": "Permission", "entries": [ - { - "name": "Read explore data", - "action_type": "read", - "object_type": "explore" - }, { "name": "Export database", "action_type": "exportContent", @@ -1068,13 +1059,6 @@ "type": "builtin", "api_keys": [{"type": "admin"}] }, - { - "slug": "ghost-explore", - "name": "Ghost Explore", - "description": "Built-in Ghost Explore integration", - "type": "core", - "api_keys": [{"type": "admin", "role": "Ghost Explore Integration"}] - }, { "slug": "self-serve-migration", "name": "Self-Serve Migration Integration", @@ -1156,7 +1140,6 @@ "authentication": "reset", "members_stripe_connect": "auth", "newsletter": "all", - "explore": "read", "comment": "all", "link": "all", "mention": "browse", @@ -1175,9 +1158,6 @@ "automation": "poll", "gift": "flushReminders" }, - "Ghost Explore Integration": { - "explore": "read" - }, "Self-Serve Migration Integration": { "db": "importContent", "content_import": "importContent", @@ -1210,7 +1190,6 @@ "product": ["browse", "read", "add", "edit"], "offer": ["browse", "read", "add", "edit"], "newsletter": ["browse", "read", "add", "edit"], - "explore": "read", "comment": "all", "link": "all", "mention": "browse",