0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-03-25 02:31:59 -05:00

Removed need for .get() with config service

no issue

The `config` service has been a source of confusion when writing with modern Ember patterns because it's use of the deprecated `ProxyMixin` forced all property access/setting to go via `.get()` and `.set()` whereas the rest of the system has mostly (there are a few other uses of ProxyObjects remaining) eliminated the use of the non-native get/set methods.

- removed use of `ProxyMixin` in the `config` service by grabbing the API response after fetching and using `Object.defineProperty()` to add native getters/setters that pass through to a tracked object holding the API response data. Ember's autotracking automatically works across the native getters/setters so we can then use the service as if it was any other native object
- updated all code to use `config.{attrName}` directly for getting/setting instead of `.get()` and `.set()`
- removed unnecessary async around `config.availableTimezones` which wasn't making any async calls
This commit is contained in:
Kevin Ansfield 2022-10-07 15:24:03 +01:00
parent 060d791a63
commit 7b443d4b63
50 changed files with 125 additions and 136 deletions

View file

@ -38,7 +38,7 @@ export default class ModalPostPreviewEmailComponent extends Component {
} }
get mailgunIsEnabled() { get mailgunIsEnabled() {
return this.config.get('mailgunIsConfigured') || return this.config.mailgunIsConfigured ||
!!(this.settings.mailgunApiKey && this.settings.mailgunDomain && this.settings.mailgunBaseUrl); !!(this.settings.mailgunApiKey && this.settings.mailgunDomain && this.settings.mailgunBaseUrl);
} }

View file

@ -57,7 +57,7 @@ export default class ModalPostPreviewSocialComponent extends Component {
urlParts.push(canonicalUrl.host); urlParts.push(canonicalUrl.host);
urlParts.push(...canonicalUrl.pathname.split('/').reject(p => !p)); urlParts.push(...canonicalUrl.pathname.split('/').reject(p => !p));
} else { } else {
const blogUrl = new URL(this.config.get('blogUrl')); const blogUrl = new URL(this.config.blogUrl);
urlParts.push(blogUrl.host); urlParts.push(blogUrl.host);
urlParts.push(...blogUrl.pathname.split('/').reject(p => !p)); urlParts.push(...blogUrl.pathname.split('/').reject(p => !p));
urlParts.push(this.args.post.slug); urlParts.push(this.args.post.slug);

View file

@ -89,7 +89,7 @@ export default class GhBillingIframe extends Component {
this.billing.getBillingIframe().contentWindow.postMessage({ this.billing.getBillingIframe().contentWindow.postMessage({
request: 'forceUpgradeInfo', request: 'forceUpgradeInfo',
response: { response: {
forceUpgrade: this.config.get('hostSettings.forceUpgrade'), forceUpgrade: this.config.hostSettings?.forceUpgrade,
isOwner: this.isOwner, isOwner: this.isOwner,
ownerUser ownerUser
} }
@ -100,11 +100,11 @@ export default class GhBillingIframe extends Component {
this.billing.set('subscription', data.subscription); this.billing.set('subscription', data.subscription);
this.billing.set('checkoutRoute', data?.checkoutRoute || '/plans'); this.billing.set('checkoutRoute', data?.checkoutRoute || '/plans');
if (data.subscription.status === 'active' && this.config.get('hostSettings.forceUpgrade')) { if (data.subscription.status === 'active' && this.config.hostSettings?.forceUpgrade) {
// config might not be updated after a subscription has been set to active. // config might not be updated after a subscription has been set to active.
// Until then assume the forceUpgrade is over and the subscription // Until then assume the forceUpgrade is over and the subscription
// was activated successfully. // was activated successfully.
this.config.set('hostSettings.forceUpgrade', false); this.config.hostSettings.forceUpgrade = false;
} }
// Detect if the current subscription is in a grace state and render a notification // Detect if the current subscription is in a grace state and render a notification

View file

@ -54,7 +54,7 @@ export default class GhPortalLinks extends Component {
init() { init() {
super.init(...arguments); super.init(...arguments);
this.siteUrl = this.config.get('blogUrl'); this.siteUrl = this.config.blogUrl;
} }
@action @action

View file

@ -137,7 +137,7 @@ export default class GhPostSettingsMenu extends Component {
// no-op, invalid URL // no-op, invalid URL
} }
} else { } else {
const blogUrl = new URL(this.config.get('blogUrl')); const blogUrl = new URL(this.config.blogUrl);
urlParts.push(blogUrl.host); urlParts.push(blogUrl.host);
urlParts.push(...blogUrl.pathname.split('/').reject(p => !p)); urlParts.push(...blogUrl.pathname.split('/').reject(p => !p));
urlParts.push(this.post.slug); urlParts.push(this.post.slug);

View file

@ -42,7 +42,7 @@ export default class Email extends Component {
'config.mailgunIsConfigured' 'config.mailgunIsConfigured'
) )
get mailgunIsEnabled() { get mailgunIsEnabled() {
return this.settings.mailgunApiKey && this.settings.mailgunDomain && this.settings.mailgunBaseUrl || this.get('config.mailgunIsConfigured'); return this.settings.mailgunApiKey && this.settings.mailgunDomain && this.settings.mailgunBaseUrl || this.config.mailgunIsConfigured;
} }
@action @action

View file

@ -19,7 +19,7 @@ export default class GhSiteIframeComponent extends Component {
} }
get srcUrl() { get srcUrl() {
const srcUrl = new URL(this.args.src || `${this.config.get('blogUrl')}/`); const srcUrl = new URL(this.args.src || `${this.config.blogUrl}/`);
if (this.args.guid) { if (this.args.guid) {
srcUrl.searchParams.set('v', this.args.guid); srcUrl.searchParams.set('v', this.args.guid);

View file

@ -19,7 +19,7 @@ export default class GhUrlPreview extends Component {
@computed('slug') @computed('slug')
get url() { get url() {
// Get the blog URL and strip the scheme // Get the blog URL and strip the scheme
let blogUrl = this.get('config.blogUrl'); let blogUrl = this.config.blogUrl;
// Remove `http[s]://` // Remove `http[s]://`
let noSchemeBlogUrl = blogUrl.substr(blogUrl.indexOf('://') + 3); let noSchemeBlogUrl = blogUrl.substr(blogUrl.indexOf('://') + 3);

View file

@ -37,8 +37,8 @@ const fetchKoenig = function () {
// required to work around ember-auto-import complaining about an unknown dynamic import // required to work around ember-auto-import complaining about an unknown dynamic import
// during the build step // during the build step
const GhostAdmin = window.GhostAdmin || window.Ember.Namespace.NAMESPACES.find(ns => ns.name === 'ghost-admin'); const GhostAdmin = window.GhostAdmin || window.Ember.Namespace.NAMESPACES.find(ns => ns.name === 'ghost-admin');
const urlTemplate = GhostAdmin.__container__.lookup('service:config').get('editor.url'); const urlTemplate = GhostAdmin.__container__.lookup('service:config').editor?.url;
const urlVersion = GhostAdmin.__container__.lookup('service:config').get('editor.version'); const urlVersion = GhostAdmin.__container__.lookup('service:config').editor?.version;
const url = new URL(urlTemplate.replace('{version}', urlVersion)); const url = new URL(urlTemplate.replace('{version}', urlVersion));
@ -96,7 +96,7 @@ export default class KoenigLexicalEditor extends Component {
// ensure we're still showing errors in development // ensure we're still showing errors in development
console.error(error); // eslint-disable-line console.error(error); // eslint-disable-line
if (this.config.get('sentry_dsn')) { if (this.config.sentry_dsn) {
Sentry.captureException(error, { Sentry.captureException(error, {
tags: { tags: {
lexical: true lexical: true

View file

@ -15,7 +15,7 @@ export default class ModalFreeMembershipSettings extends ModalBase {
@tracked siteUrl; @tracked siteUrl;
init() { init() {
super.init(...arguments); super.init(...arguments);
this.siteUrl = this.config.get('blogUrl'); this.siteUrl = this.config.blogUrl;
} }
@action @action

View file

@ -335,7 +335,7 @@ export default ModalComponent.extend({
this.set('customIcon', this.settings.portalButtonIcon); this.set('customIcon', this.settings.portalButtonIcon);
} }
this.siteUrl = this.config.get('blogUrl'); this.siteUrl = this.config.blogUrl;
this.set('isPreloading', false); this.set('isPreloading', false);
}), }),

View file

@ -107,7 +107,7 @@ export default class ModalTierPrice extends ModalBase {
} }
get siteUrl() { get siteUrl() {
return this.config.get('blogUrl'); return this.config.blogUrl;
} }
// eslint-disable-next-line no-dupe-class-members // eslint-disable-next-line no-dupe-class-members

View file

@ -17,7 +17,7 @@ export default class LinkOfferModal extends Component {
get offerUrl() { get offerUrl() {
const code = this.args.data.offer?.code || ''; const code = this.args.data.offer?.code || '';
if (code) { if (code) {
const siteUrl = this.config.get('blogUrl'); const siteUrl = this.config.blogUrl;
return `${siteUrl}/${code}`; return `${siteUrl}/${code}`;
} }
return ''; return '';

View file

@ -21,11 +21,11 @@ export default class AboutModal extends Component {
get linkToGitHubReleases() { get linkToGitHubReleases() {
// Don't link to GitHub Releases if the version contains the // Don't link to GitHub Releases if the version contains the
// pre-release identifier // pre-release identifier
return !this.config.get('version').includes('-pre.'); return !this.config.version.includes('-pre.');
} }
get showSystemInfo() { get showSystemInfo() {
const isPro = !!this.config.get('hostSettings')?.siteId; const isPro = !!this.config.hostSettings?.siteId;
// Don't show any system info for Pro // Don't show any system info for Pro
if (isPro) { if (isPro) {
@ -36,8 +36,8 @@ export default class AboutModal extends Component {
} }
get showDatabaseWarning() { get showDatabaseWarning() {
const isProduction = !!this.config.get('environment').match?.(/production/i); const isProduction = !!this.config.environment.match?.(/production/i);
const database = this.config.get('database'); const database = this.config.database;
// Show a warning if we're in production and not using MySQL 8 // Show a warning if we're in production and not using MySQL 8
if (isProduction && database !== 'mysql8') { if (isProduction && database !== 'mysql8') {

View file

@ -182,7 +182,7 @@ export default class StripeSettingsForm extends Component {
@action @action
updateStripeDirect() { updateStripeDirect() {
// Allow disabling stripe direct keys if stripe is still enabled, while the config is disabled // Allow disabling stripe direct keys if stripe is still enabled, while the config is disabled
this.stripeDirect = this.config.get('stripeDirect') this.stripeDirect = this.config.stripeDirect
|| (this.membersUtils.isStripeEnabled && !this.settings.stripeConnectAccountId); || (this.membersUtils.isStripeEnabled && !this.settings.stripeConnectAccountId);
} }

View file

@ -49,7 +49,7 @@ export default class TagForm extends Component {
} }
get seoURL() { get seoURL() {
const blogUrl = this.config.get('blogUrl'); const blogUrl = this.config.blogUrl;
const seoSlug = this.args.tag.slug || ''; const seoSlug = this.args.tag.slug || '';
let seoURL = this.args.tag.canonicalUrl || `${blogUrl}/tag/${seoSlug}`; let seoURL = this.args.tag.canonicalUrl || `${blogUrl}/tag/${seoSlug}`;

View file

@ -25,7 +25,7 @@ export default class OffersController extends Controller {
@tracked tiers = []; @tracked tiers = [];
@tracked portalPreviewUrl = ''; @tracked portalPreviewUrl = '';
@tracked defaultSiteUrl = this.config.get('blogUrl'); @tracked defaultSiteUrl = this.config.blogUrl;
@tracked durations = [ @tracked durations = [
{ {
@ -350,7 +350,7 @@ export default class OffersController extends Controller {
get offerUrl() { get offerUrl() {
const code = this.offer?.code || ''; const code = this.offer?.code || '';
if (code) { if (code) {
const siteUrl = this.config.get('blogUrl'); const siteUrl = this.config.blogUrl;
return `${siteUrl}/${slugify(code)}`; return `${siteUrl}/${slugify(code)}`;
} }
return ''; return '';

View file

@ -32,13 +32,13 @@ export default class GeneralController extends Controller {
@tracked scratchValues = new TrackedObject(); @tracked scratchValues = new TrackedObject();
availableTimezones = null; availableTimezones = this.config.availableTimezones;
imageExtensions = IMAGE_EXTENSIONS; imageExtensions = IMAGE_EXTENSIONS;
imageMimeTypes = IMAGE_MIME_TYPES; imageMimeTypes = IMAGE_MIME_TYPES;
@computed('config.blogUrl', 'settings.publicHash') @computed('config.blogUrl', 'settings.publicHash')
get privateRSSUrl() { get privateRSSUrl() {
let blogUrl = this.get('config.blogUrl'); let blogUrl = this.config.blogUrl;
let publicHash = this.settings.publicHash; let publicHash = this.settings.publicHash;
return `${blogUrl}/${publicHash}/rss`; return `${blogUrl}/${publicHash}/rss`;

View file

@ -10,7 +10,7 @@ export default class IntegrationsController extends Controller {
_allIntegrations = this.store.peekAll('integration'); _allIntegrations = this.store.peekAll('integration');
get zapierDisabled() { get zapierDisabled() {
return this.config.get('hostSettings.limits.customIntegrations.disabled'); return this.config.hostSettings?.limits?.customIntegrations?.disabled;
} }
// filter over the live query so that the list is automatically updated // filter over the live query so that the list is automatically updated

View file

@ -59,7 +59,7 @@ export default class MembersAccessController extends Controller {
} }
get siteUrl() { get siteUrl() {
return this.config.get('blogUrl'); return this.config.blogUrl;
} }
get selectedCurrency() { get selectedCurrency() {
@ -67,7 +67,7 @@ export default class MembersAccessController extends Controller {
} }
get isConnectDisallowed() { get isConnectDisallowed() {
const siteUrl = this.config.get('blogUrl'); const siteUrl = this.config.blogUrl;
return envConfig.environment !== 'development' && !/^https:/.test(siteUrl); return envConfig.environment !== 'development' && !/^https:/.test(siteUrl);
} }
@ -383,7 +383,7 @@ export default class MembersAccessController extends Controller {
} }
_validateSignupRedirect(url, type) { _validateSignupRedirect(url, type) {
const siteUrl = this.config.get('blogUrl'); const siteUrl = this.config.blogUrl;
let errMessage = `Please enter a valid URL`; let errMessage = `Please enter a valid URL`;
this.settings.errors.remove(type); this.settings.errors.remove(type);
this.settings.hasValidated.removeObject(type); this.settings.hasValidated.removeObject(type);

View file

@ -28,7 +28,7 @@ export default class NavigationController extends Controller {
@computed('config.blogUrl') @computed('config.blogUrl')
get blogUrl() { get blogUrl() {
let url = this.get('config.blogUrl'); let url = this.config.blogUrl;
return url.slice(-1) !== '/' ? `${url}/` : url; return url.slice(-1) !== '/' ? `${url}/` : url;
} }

View file

@ -17,7 +17,7 @@ export default class TierController extends Controller {
constructor() { constructor() {
super(...arguments); super(...arguments);
this.siteUrl = this.config.get('blogUrl'); this.siteUrl = this.config.blogUrl;
} }
get tier() { get tier() {

View file

@ -21,7 +21,7 @@ export default class TiersController extends Controller {
} }
setIconStyle() { setIconStyle() {
let icon = this.config.get('icon'); let icon = this.config.icon;
if (icon) { if (icon) {
return htmlSafe(`background-image: url(${icon})`); return htmlSafe(`background-image: url(${icon})`);
} }

View file

@ -8,7 +8,7 @@ export default class AccentColorBackgroundHelper extends Helper {
@service config; @service config;
compute() { compute() {
const color = this.get('config.accent_color'); const color = this.config.accent_color;
return htmlSafe(`background: ${color};`); return htmlSafe(`background: ${color};`);
} }
} }

View file

@ -5,6 +5,6 @@ export default class EnableDeveloperExperimentsHelper extends Helper {
@service config; @service config;
compute() { compute() {
return this.config.get('enableDeveloperExperiments'); return this.config.enableDeveloperExperiments;
} }
} }

View file

@ -8,7 +8,7 @@ export default class SiteIconStyleHelper extends Helper {
@service config; @service config;
compute() { compute() {
const icon = this.get('config.icon') || 'https://static.ghost.org/v4.0.0/images/ghost-orb-2.png'; const icon = this.config.icon || 'https://static.ghost.org/v4.0.0/images/ghost-orb-2.png';
return htmlSafe(`background-image: url(${icon})`); return htmlSafe(`background-image: url(${icon})`);
} }
} }

View file

@ -235,7 +235,7 @@ export default Model.extend(Comparable, ValidationEngine, {
}), }),
previewUrl: computed('uuid', 'ghostPaths.url', 'config.blogUrl', function () { previewUrl: computed('uuid', 'ghostPaths.url', 'config.blogUrl', function () {
let blogUrl = this.get('config.blogUrl'); let blogUrl = this.config.blogUrl;
let uuid = this.uuid; let uuid = this.uuid;
// routeKeywords.preview: 'p' // routeKeywords.preview: 'p'
let previewKeyword = 'p'; let previewKeyword = 'p';

View file

@ -157,11 +157,11 @@ export default Route.extend(ShortcutsRoute, {
// init Sentry here rather than app.js so that we can use API-supplied // init Sentry here rather than app.js so that we can use API-supplied
// sentry_dsn and sentry_env rather than building it into release assets // sentry_dsn and sentry_env rather than building it into release assets
if (this.config.get('sentry_dsn')) { if (this.config.sentry_dsn) {
InitSentryForEmber({ InitSentryForEmber({
dsn: this.config.get('sentry_dsn'), dsn: this.config.sentry_dsn,
environment: this.config.get('sentry_env'), environment: this.config.sentry_env,
release: `ghost@${this.config.get('version')}`, release: `ghost@${this.config.version}`,
beforeSend(event) { beforeSend(event) {
event.tags = event.tags || {}; event.tags = event.tags || {};
event.tags.shown_to_user = event.tags.shown_to_user || false; event.tags.shown_to_user = event.tags.shown_to_user || false;
@ -181,7 +181,7 @@ export default Route.extend(ShortcutsRoute, {
await this.session.postAuthPreparation(); await this.session.postAuthPreparation();
} }
if (this.config.get('hostSettings.forceUpgrade')) { if (this.config.hostSettings?.forceUpgrade) {
// enforce opening the BMA in a force upgrade state // enforce opening the BMA in a force upgrade state
this.billing.openBillingWindow(this.router.currentURL, '/pro'); this.billing.openBillingWindow(this.router.currentURL, '/pro');
} }

View file

@ -7,7 +7,7 @@ export default class DesignsandboxRoute extends Route {
beforeModel() { beforeModel() {
super.beforeModel(...arguments); super.beforeModel(...arguments);
if (!this.config.get('enableDeveloperExperiments')) { if (!this.config.enableDeveloperExperiments) {
return this.transitionTo('home'); return this.transitionTo('home');
} }
} }

View file

@ -13,7 +13,7 @@ export default AuthenticatedRoute.extend({
classNames: ['editor'], classNames: ['editor'],
beforeModel() { beforeModel() {
if (!this.config.get('editor.url')) { if (!this.config.editor?.url) {
return this.router.transitionTo('posts'); return this.router.transitionTo('posts');
} }
}, },

View file

@ -15,7 +15,7 @@ export default class ProRoute extends AuthenticatedRoute {
super.beforeModel(...arguments); super.beforeModel(...arguments);
// allow non-owner users to access the BMA when we're in a force upgrade state // allow non-owner users to access the BMA when we're in a force upgrade state
if (!this.session.user.isOwnerOnly && !this.config.get('hostSettings.forceUpgrade')) { if (!this.session.user.isOwnerOnly && !this.config.hostSettings?.forceUpgrade) {
return this.transitionTo('home'); return this.transitionTo('home');
} }

View file

@ -11,15 +11,10 @@ export default class GeneralSettingsRoute extends AdminRoute {
model() { model() {
return RSVP.hash({ return RSVP.hash({
settings: this.settings.reload(), settings: this.settings.reload()
availableTimezones: this.config.get('availableTimezones')
}); });
} }
setupController(controller, models) {
controller.set('availableTimezones', models.availableTimezones);
}
deactivate() { deactivate() {
this.confirmModal = null; this.confirmModal = null;
this.hasConfirmed = false; this.hasConfirmed = false;

View file

@ -18,7 +18,7 @@ export default class ZapierRoute extends AdminRoute {
beforeModel() { beforeModel() {
super.beforeModel(...arguments); super.beforeModel(...arguments);
if (this.config.get('hostSettings.limits.customIntegrations.disabled')) { if (this.config.hostSettings?.limits?.customIntegrations?.disabled) {
return this.transitionTo('settings.integrations'); return this.transitionTo('settings.integrations');
} }
} }

View file

@ -72,7 +72,7 @@ export default class SignupRoute extends UnauthenticatedRoute {
} }
// set blogTitle, so password validation has access to it // set blogTitle, so password validation has access to it
signupDetails.blogTitle = this.config.get('blogTitle'); signupDetails.blogTitle = this.config.blogTitle;
resolve(signupDetails); resolve(signupDetails);
}).catch(() => { }).catch(() => {

View file

@ -236,7 +236,7 @@ class ajaxService extends AjaxService {
const result = await makeRequest(hash); const result = await makeRequest(hash);
success = true; success = true;
if (attempts !== 0 && this.config.get('sentry_dsn')) { if (attempts !== 0 && this.config.sentry_dsn) {
captureMessage('Request took multiple attempts', {extra: getErrorData()}); captureMessage('Request took multiple attempts', {extra: getErrorData()});
} }
@ -254,7 +254,7 @@ class ajaxService extends AjaxService {
if (retryErrorChecks.some(check => check(error.response)) && retryingMs <= maxRetryingMs) { if (retryErrorChecks.some(check => check(error.response)) && retryingMs <= maxRetryingMs) {
await timeout(retryPeriods[attempts] || retryPeriods[retryPeriods.length - 1]); await timeout(retryPeriods[attempts] || retryPeriods[retryPeriods.length - 1]);
attempts += 1; attempts += 1;
} else if (attempts > 0 && this.config.get('sentry_dsn')) { } else if (attempts > 0 && this.config.sentry_dsn) {
captureMessage('Request failed after multiple attempts', {extra: getErrorData()}); captureMessage('Request failed after multiple attempts', {extra: getErrorData()});
throw error; throw error;
} else { } else {

View file

@ -19,7 +19,7 @@ export default class BillingService extends Service {
init() { init() {
super.init(...arguments); super.init(...arguments);
if (this.config.get('hostSettings.billing.url')) { if (this.config.hostSettings?.billing?.url) {
window.addEventListener('message', (event) => { window.addEventListener('message', (event) => {
if (event && event.data && event.data.route) { if (event && event.data && event.data.route) {
this.handleRouteChangeInIframe(event.data.route); this.handleRouteChangeInIframe(event.data.route);
@ -46,7 +46,7 @@ export default class BillingService extends Service {
// initiate getting owner user in the background // initiate getting owner user in the background
this.getOwnerUser(); this.getOwnerUser();
let url = this.config.get('hostSettings.billing.url'); let url = this.config.hostSettings?.billing?.url;
if (window.location.hash && window.location.hash.includes(this.billingRouteRoot)) { if (window.location.hash && window.location.hash.includes(this.billingRouteRoot)) {
let destinationRoute = window.location.hash.replace(this.billingRouteRoot, ''); let destinationRoute = window.location.hash.replace(this.billingRouteRoot, '');

View file

@ -1,26 +1,17 @@
import Ember from 'ember';
import RSVP from 'rsvp'; import RSVP from 'rsvp';
import Service, {inject as service} from '@ember/service'; import Service, {inject as service} from '@ember/service';
import classic from 'ember-classic-decorator';
import timezoneData from '@tryghost/timezone-data'; import timezoneData from '@tryghost/timezone-data';
import {computed} from '@ember/object'; import {TrackedObject} from 'tracked-built-ins';
import {tracked} from '@glimmer/tracking';
// ember-cli-shims doesn't export _ProxyMixin export default class ConfigService extends Service {
const {_ProxyMixin} = Ember;
@classic
export default class ConfigService extends Service.extend(_ProxyMixin) {
@service ajax; @service ajax;
@service ghostPaths; @service ghostPaths;
@service session; @service session;
content = null; @tracked content = new TrackedObject();
init() { availableTimezones = timezoneData;
super.init(...arguments);
this.content = {};
}
fetch() { fetch() {
let promises = []; let promises = [];
@ -34,48 +25,38 @@ export default class ConfigService extends Service.extend(_ProxyMixin) {
return RSVP.all(promises); return RSVP.all(promises);
} }
fetchUnauthenticated() { async fetchUnauthenticated() {
let siteUrl = this.ghostPaths.url.api('site'); const siteUrl = this.ghostPaths.url.api('site');
return this.ajax.request(siteUrl).then(({site}) => { const {site} = await this.ajax.request(siteUrl);
// normalize url to non-trailing-slash
site.blogUrl = site.url.replace(/\/$/, '');
site.blogTitle = site.title;
delete site.url;
delete site.title;
Object.assign(this.content, site); // normalize url to non-trailing-slash
}).then(() => { site.blogUrl = site.url.replace(/\/$/, '');
this.notifyPropertyChange('content'); site.blogTitle = site.title;
}); delete site.url;
delete site.title;
Object.assign(this.content, site);
this._defineProperties(site);
} }
fetchAuthenticated() { async fetchAuthenticated() {
let configUrl = this.ghostPaths.url.api('config'); const configUrl = this.ghostPaths.url.api('config');
return this.ajax.request(configUrl).then(({config}) => { const {config} = await this.ajax.request(configUrl);
Object.assign(this.content, config);
}).then(() => { Object.assign(this.content, config);
this.notifyPropertyChange('content'); this._defineProperties(config);
});
} }
@computed
get availableTimezones() {
return RSVP.resolve(timezoneData);
}
@computed('blogUrl')
get blogDomain() { get blogDomain() {
let blogUrl = this.get('blogUrl'); const blogDomain = this.blogUrl
let blogDomain = blogUrl
.replace(/^https?:\/\//, '') .replace(/^https?:\/\//, '')
.replace(/\/?$/, ''); .replace(/\/?$/, '');
return blogDomain; return blogDomain;
} }
@computed('blogDomain')
get emailDomain() { get emailDomain() {
let blogDomain = this.blogDomain || ''; const blogDomain = this.blogDomain || '';
const domainExp = blogDomain.match(new RegExp('^([^/:?#]+)(?:[/:?#]|$)', 'i')); const domainExp = blogDomain.match(new RegExp('^([^/:?#]+)(?:[/:?#]|$)', 'i'));
const domain = (domainExp && domainExp[1]) || ''; const domain = (domainExp && domainExp[1]) || '';
if (domain.startsWith('www.')) { if (domain.startsWith('www.')) {
@ -85,10 +66,25 @@ export default class ConfigService extends Service.extend(_ProxyMixin) {
} }
getSiteUrl(path) { getSiteUrl(path) {
const siteUrl = new URL(this.get('blogUrl')); const siteUrl = new URL(this.blogUrl);
const subdir = siteUrl.pathname.endsWith('/') ? siteUrl.pathname : `${siteUrl.pathname}/`; const subdir = siteUrl.pathname.endsWith('/') ? siteUrl.pathname : `${siteUrl.pathname}/`;
const fullPath = `${subdir}${path.replace(/^\//, '')}`; const fullPath = `${subdir}${path.replace(/^\//, '')}`;
return `${siteUrl.origin}${fullPath}`; return `${siteUrl.origin}${fullPath}`;
} }
_defineProperties(obj) {
for (const name of Object.keys(obj)) {
if (!Object.prototype.hasOwnProperty.call(this, name)) {
Object.defineProperty(this, name, {
get() {
return this.content[name];
},
set(newValue) {
this.content[name] = newValue;
}
});
}
}
}
} }

View file

@ -15,7 +15,7 @@ export default class FrontendService extends Service {
} }
getUrl(path) { getUrl(path) {
const siteUrl = new URL(this.config.get('blogUrl')); const siteUrl = new URL(this.config.blogUrl);
const subdir = siteUrl.pathname.endsWith('/') ? siteUrl.pathname : `${siteUrl.pathname}/`; const subdir = siteUrl.pathname.endsWith('/') ? siteUrl.pathname : `${siteUrl.pathname}/`;
const fullPath = `${subdir}${path.replace(/^\//, '')}`; const fullPath = `${subdir}${path.replace(/^\//, '')}`;

View file

@ -31,7 +31,7 @@ export default class LimitsService extends Service {
constructor() { constructor() {
super(...arguments); super(...arguments);
let limits = this.config.get('hostSettings.limits'); let limits = this.config.hostSettings?.limits;
this.limiter = new LimitService(); this.limiter = new LimitService();
@ -41,10 +41,10 @@ export default class LimitsService extends Service {
let helpLink; let helpLink;
if (this.config.get('hostSettings.billing.enabled') if (this.config.hostSettings?.billing?.enabled === true
&& this.config.get('hostSettings.billing.enabled') === true && this.config.hostSettings?.billing?.url
&& this.config.get('hostSettings.billing.url')) { ) {
helpLink = this.config.get('hostSettings.billing.url'); helpLink = this.config.hostSettings.billing?.url;
} else { } else {
helpLink = 'https://ghost.org/help/'; helpLink = 'https://ghost.org/help/';
} }

View file

@ -22,7 +22,7 @@ export default class MembersUtilsService extends Service {
* Note: always use paidMembersEnabled! Only use this getter for the Stripe Connection UI. * Note: always use paidMembersEnabled! Only use this getter for the Stripe Connection UI.
*/ */
get isStripeEnabled() { get isStripeEnabled() {
const stripeDirect = this.config.get('stripeDirect'); const stripeDirect = this.config.stripeDirect;
const hasDirectKeys = !!this.settings.stripeSecretKey && !!this.settings.stripePublishableKey; const hasDirectKeys = !!this.settings.stripeSecretKey && !!this.settings.stripePublishableKey;
const hasConnectKeys = !!this.settings.stripeConnectSecretKey && !!this.settings.stripeConnectPublishableKey; const hasConnectKeys = !!this.settings.stripeConnectSecretKey && !!this.settings.stripeConnectPublishableKey;
@ -111,7 +111,7 @@ export default class MembersUtilsService extends Service {
return t.visibility === 'public' && t.type === 'paid'; return t.visibility === 'public' && t.type === 'paid';
}).map(t => t.id); }).map(t => t.id);
const baseUrl = this.config.get('blogUrl'); const baseUrl = this.config.blogUrl;
const portalBase = '/#/portal/preview'; const portalBase = '/#/portal/preview';
const settingsParam = new URLSearchParams(); const settingsParam = new URLSearchParams();
const signupButtonText = this.settings.portalButtonSignupText || ''; const signupButtonText = this.settings.portalButtonSignupText || '';
@ -177,7 +177,7 @@ export default class MembersUtilsService extends Service {
tierId tierId
} = overrides; } = overrides;
const baseUrl = this.config.get('blogUrl'); const baseUrl = this.config.blogUrl;
const portalBase = '/#/portal/preview/offer'; const portalBase = '/#/portal/preview/offer';
const settingsParam = new URLSearchParams(); const settingsParam = new URLSearchParams();

View file

@ -95,7 +95,7 @@ export default class NotificationsService extends Service {
options = options || {}; options = options || {};
if (!options.isApiError && (!options.type || options.type === 'error')) { if (!options.isApiError && (!options.type || options.type === 'error')) {
if (this.config.get('sentry_dsn')) { if (this.config.sentry_dsn) {
// message could be a htmlSafe object rather than a string // message could be a htmlSafe object rather than a string
const displayedMessage = message.string || message; const displayedMessage = message.string || message;
@ -188,7 +188,7 @@ export default class NotificationsService extends Service {
msg = `${msg} ${resp.context}`; msg = `${msg} ${resp.context}`;
} }
if (this.config.get('sentry_dsn')) { if (this.config.sentry_dsn) {
const reportedError = resp instanceof Error ? resp : msg; const reportedError = resp instanceof Error ? resp : msg;
Sentry.captureException(reportedError, { Sentry.captureException(reportedError, {

View file

@ -43,13 +43,13 @@ export default class SessionService extends ESASessionService {
await this.frontend.loginIfNeeded(); await this.frontend.loginIfNeeded();
// update Sentry with the full Ghost version which we only get after authentication // update Sentry with the full Ghost version which we only get after authentication
if (this.config.get('sentry_dsn')) { if (this.config.sentry_dsn) {
configureScope((scope) => { configureScope((scope) => {
scope.addEventProcessor((event) => { scope.addEventProcessor((event) => {
return new Promise((resolve) => { return new Promise((resolve) => {
resolve({ resolve({
...event, ...event,
release: `ghost@${this.config.get('version')}` release: `ghost@${this.config.version}`
}); });
}); });
}); });

View file

@ -25,11 +25,11 @@ export default class TenorService extends Service {
_nextPos = null; _nextPos = null;
get apiKey() { get apiKey() {
return this.config.get('tenor.googleApiKey'); return this.config.tenor.googleApiKey;
} }
get contentfilter() { get contentfilter() {
return this.config.get('tenor.contentFilter') || 'off'; return this.config.tenor.contentFilter || 'off';
} }
get isLoading() { get isLoading() {

View file

@ -144,7 +144,7 @@ export default class UiService extends Service {
currentRoute = currentRoute.parent; currentRoute = currentRoute.parent;
} }
let blogTitle = this.config.get('blogTitle'); let blogTitle = this.config.blogTitle;
if (!isEmpty(tokens)) { if (!isEmpty(tokens)) {
window.document.title = `${tokens.join(' - ')} - ${blogTitle}`; window.document.title = `${tokens.join(' - ')} - ${blogTitle}`;

View file

@ -1,5 +1,5 @@
import moment from 'moment-timezone'; import moment from 'moment-timezone';
import {action, get} from '@ember/object'; import {action} from '@ember/object';
import {htmlSafe} from '@ember/template'; import {htmlSafe} from '@ember/template';
import {task} from 'ember-concurrency'; import {task} from 'ember-concurrency';
import {tracked} from '@glimmer/tracking'; import {tracked} from '@glimmer/tracking';
@ -131,7 +131,7 @@ export default class PublishOptions {
get mailgunIsConfigured() { get mailgunIsConfigured() {
return this.settings.mailgunIsConfigured return this.settings.mailgunIsConfigured
|| get(this.config, 'mailgunIsConfigured'); || this.config.mailgunIsConfigured;
} }
@action @action

View file

@ -12,7 +12,7 @@ Route.reopen({
transition.abort(); transition.abort();
this.upgradeStatus.requireUpgrade(); this.upgradeStatus.requireUpgrade();
return false; return false;
} else if (this.config.get('hostSettings.forceUpgrade')) { } else if (this.config.hostSettings?.forceUpgrade) {
// Do not prevent transitions to the BMA or to signout // Do not prevent transitions to the BMA or to signout
if (transition.to?.name === 'pro.index' || transition.to?.name === 'signout') { if (transition.to?.name === 'pro.index' || transition.to?.name === 'signout') {
return true; return true;

View file

@ -123,7 +123,7 @@ export default class KoenigLinkInput extends Component {
// prevent Enter from triggering in the editor and removing text // prevent Enter from triggering in the editor and removing text
event.preventDefault(); event.preventDefault();
let href = relativeToAbsolute(this._href, this.config.get('blogUrl')); let href = relativeToAbsolute(this._href, this.config.blogUrl);
this.set('_href', href); this.set('_href', href);
if (this.source === 'direct') { if (this.source === 'direct') {

View file

@ -169,7 +169,7 @@ export default class KoenigLinkToolbar extends Component {
// on the configured blog url // on the configured blog url
this._target = target; this._target = target;
let href = target.getAttribute('href'); let href = target.getAttribute('href');
let blogUrl = this.config.get('blogUrl'); let blogUrl = this.config.blogUrl;
this.set('url', relativeToAbsolute(href, blogUrl)); this.set('url', relativeToAbsolute(href, blogUrl));
this.set('showToolbar', true); this.set('showToolbar', true);
run.schedule('afterRender', this, function () { run.schedule('afterRender', this, function () {

View file

@ -22,7 +22,7 @@ export default class CardIsAvailableHelper extends Helper {
} }
if (card.developerExperiment) { if (card.developerExperiment) {
cardIsAvailable = cardIsAvailable && this.config.get('enableDeveloperExperiments'); cardIsAvailable = cardIsAvailable && this.config.enableDeveloperExperiments;
} }
if (postType && card.postType) { if (postType && card.postType) {

View file

@ -18,17 +18,15 @@ describe('Integration: Service: config', function () {
server.shutdown(); server.shutdown();
}); });
it('returns a list of timezones in the expected format', function (done) { it('returns a list of timezones in the expected format', function () {
let service = this.owner.lookup('service:config'); const service = this.owner.lookup('service:config');
const timezones = service.availableTimezones;
service.get('availableTimezones').then(function (timezones) { expect(timezones.length).to.equal(66);
expect(timezones.length).to.equal(66); expect(timezones[0].name).to.equal('Pacific/Pago_Pago');
expect(timezones[0].name).to.equal('Pacific/Pago_Pago'); expect(timezones[0].label).to.equal('(GMT -11:00) Midway Island, Samoa');
expect(timezones[0].label).to.equal('(GMT -11:00) Midway Island, Samoa'); expect(timezones[1].name).to.equal('Pacific/Honolulu');
expect(timezones[1].name).to.equal('Pacific/Honolulu'); expect(timezones[1].label).to.equal('(GMT -10:00) Hawaii');
expect(timezones[1].label).to.equal('(GMT -10:00) Hawaii');
done();
});
}); });
it('normalizes blogUrl to non-trailing-slash', function (done) { it('normalizes blogUrl to non-trailing-slash', function (done) {