Removed need for .get() with settings service

no issue

The `settings` 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 `settings` service by grabbing the attributes off the setting model after fetching and using `Object.defineProperty()` to add native getters/setters that pass through to the model's getters/setters. 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 `settings.{attrName}` directly for getting/setting instead of `.get()` and `.set()`
- removed use of observer in the `customViews` service because it was being set up before the native properties had been added on the settings service meaning autotracking wasn't able to set up properly
This commit is contained in:
Kevin Ansfield 2022-10-07 14:23:21 +01:00
parent b5fd02c9e8
commit 060d791a63
72 changed files with 368 additions and 374 deletions

View File

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

View File

@ -24,7 +24,7 @@ export default class ModalPostPreviewSocialComponent extends Component {
get _fallbackDescription() { get _fallbackDescription() {
return this.args.post.customExcerpt || return this.args.post.customExcerpt ||
this.serpDescription || this.serpDescription ||
this.settings.get('description'); this.settings.description;
} }
@action @action
@ -107,7 +107,7 @@ export default class ModalPostPreviewSocialComponent extends Component {
} }
get facebookImage() { get facebookImage() {
return this.args.post.ogImage || this.args.post.featureImage || this.settings.get('ogImage') || this.settings.get('coverImage'); return this.args.post.ogImage || this.args.post.featureImage || this.settings.ogImage || this.settings.coverImage;
} }
@action @action
@ -166,7 +166,7 @@ export default class ModalPostPreviewSocialComponent extends Component {
} }
get twitterImage() { get twitterImage() {
return this.args.post.twitterImage || this.args.post.featureImage || this.settings.get('twitterImage') || this.settings.get('coverImage'); return this.args.post.twitterImage || this.args.post.featureImage || this.settings.twitterImage || this.settings.coverImage;
} }
@action @action

View File

@ -66,7 +66,7 @@ export default class PublishFlowOptions extends Component {
} }
if (this.args.publishOptions.isScheduled) { if (this.args.publishOptions.isScheduled) {
const scheduleMoment = moment.tz(this.args.publishOptions.scheduledAtUTC, this.settings.get('timezone')); const scheduleMoment = moment.tz(this.args.publishOptions.scheduledAtUTC, this.settings.timezone);
buttonText += `, on ${scheduleMoment.format('MMMM Do')}`; buttonText += `, on ${scheduleMoment.format('MMMM Do')}`;
} else { } else {
buttonText += ', right now'; buttonText += ', right now';

View File

@ -9,7 +9,7 @@ export default class PublishAtOption extends Component {
@action @action
setDate(selectedDate) { setDate(selectedDate) {
// selectedDate is a Date object that contains the correct date string in the blog timezone // selectedDate is a Date object that contains the correct date string in the blog timezone
const selectedMoment = moment.tz(selectedDate, this.settings.get('timezone')); const selectedMoment = moment.tz(selectedDate, this.settings.timezone);
const {years, months, date} = selectedMoment.toObject(); const {years, months, date} = selectedMoment.toObject();
// Create a new moment from existing scheduledAtUTC _in site timezone_. // Create a new moment from existing scheduledAtUTC _in site timezone_.
@ -17,7 +17,7 @@ export default class PublishAtOption extends Component {
// to account for the converted UTC date being yesterday/tomorrow. // to account for the converted UTC date being yesterday/tomorrow.
const newDate = moment.tz( const newDate = moment.tz(
this.args.publishOptions.scheduledAtUTC, this.args.publishOptions.scheduledAtUTC,
this.settings.get('timezone') this.settings.timezone
); );
newDate.set({years, months, date}); newDate.set({years, months, date});
@ -27,7 +27,7 @@ export default class PublishAtOption extends Component {
@action @action
setTime(time, event) { setTime(time, event) {
const newDate = moment.tz(this.args.publishOptions.scheduledAtUTC, this.settings.get('timezone')); const newDate = moment.tz(this.args.publishOptions.scheduledAtUTC, this.settings.timezone);
// used to reset the time value on blur if it's invalid // used to reset the time value on blur if it's invalid
const oldTime = newDate.format('HH:mm'); const oldTime = newDate.format('HH:mm');

View File

@ -32,7 +32,7 @@ export default class GhEditorPostStatusComponent extends Component {
return formatPostTime( return formatPostTime(
this.args.post.publishedAtUTC, this.args.post.publishedAtUTC,
{timezone: this.settings.get('timezone'), scheduled: true} {timezone: this.settings.timezone, scheduled: true}
); );
} }

View File

@ -1,14 +1,11 @@
import Component from '@glimmer/component'; import Component from '@glimmer/component';
import validator from 'validator'; import validator from 'validator';
import {action} from '@ember/object'; import {action} from '@ember/object';
import {get, set} from '@ember/object';
export default class GhFacebookUrlInput extends Component { export default class GhFacebookUrlInput extends Component {
// NOTE: `get` and `set` are required when reading/writing model properties
// because we can be dealing with proxy objects such as the settings service
get value() { get value() {
const {model, modelProperty, scratchValue} = this.args; const {model, modelProperty, scratchValue} = this.args;
return scratchValue || get(model, modelProperty); return scratchValue || model[modelProperty];
} }
@action @action
@ -28,7 +25,7 @@ export default class GhFacebookUrlInput extends Component {
if (!newUrl) { if (!newUrl) {
// Clear out the Facebook url // Clear out the Facebook url
set(model, modelProperty, null); model[modelProperty] = null;
this.args.setScratchValue?.(null); this.args.setScratchValue?.(null);
return; return;
} }
@ -51,7 +48,7 @@ export default class GhFacebookUrlInput extends Component {
throw 'invalid url'; throw 'invalid url';
} }
set(model, modelProperty, newUrl); model[modelProperty] = newUrl;
this.args.setScratchValue?.(null); this.args.setScratchValue?.(null);
} catch (e) { } catch (e) {
if (e === 'invalid url') { if (e === 'invalid url') {

View File

@ -21,7 +21,7 @@ export default class GhKoenigEditorReactComponent extends Component {
} }
get accentColor() { get accentColor() {
const color = this.settings.get('accentColor'); const color = this.settings.accentColor;
if (color && color[0] === '#') { if (color && color[0] === '#') {
return color.slice(1); return color.slice(1);
} }

View File

@ -164,7 +164,7 @@ export default class GhMarkdownEditor extends Component.extend(ShortcutsMixin) {
toolbar.splice(index, 1); toolbar.splice(index, 1);
} }
if (this.get('settings.unsplash')) { if (this.settings.unsplash) {
let image = toolbar.findBy('name', 'image'); let image = toolbar.findBy('name', 'image');
let index = toolbar.indexOf(image) + 1; let index = toolbar.indexOf(image) + 1;

View File

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

View File

@ -12,13 +12,13 @@ const VISIBILITIES = [
@classic @classic
export default class GhPsmVisibilityInput extends Component { export default class GhPsmVisibilityInput extends Component {
@service settings; @service settings;
// public attrs // public attrs
post = null; post = null;
@computed('post.visibility') @computed('post.visibility')
get selectedVisibility() { get selectedVisibility() {
return this.get('post.visibility') || this.settings.get('defaultContentVisibility'); return this.get('post.visibility') || this.settings.defaultContentVisibility;
} }
init() { init() {

View File

@ -1,13 +1,10 @@
import Component from '@glimmer/component'; import Component from '@glimmer/component';
import {action} from '@ember/object'; import {action} from '@ember/object';
import {get, set} from '@ember/object';
export default class GhTwitterUrlInput extends Component { export default class GhTwitterUrlInput extends Component {
// NOTE: `get` and `set` are required when reading/writing model properties
// because we can be dealing with proxy objects such as the settings service
get value() { get value() {
const {model, modelProperty, scratchValue} = this.args; const {model, modelProperty, scratchValue} = this.args;
return scratchValue || get(model, modelProperty); return scratchValue || model[modelProperty];
} }
@action @action
@ -27,7 +24,7 @@ export default class GhTwitterUrlInput extends Component {
if (!newUrl) { if (!newUrl) {
// Clear out the Twitter url // Clear out the Twitter url
set(model, modelProperty, ''); model[modelProperty] = '';
this.args.setScratchValue?.(null); this.args.setScratchValue?.(null);
return; return;
} }
@ -52,7 +49,7 @@ export default class GhTwitterUrlInput extends Component {
return; return;
} }
set(model, modelProperty, `https://twitter.com/${username}`); model[modelProperty] = `https://twitter.com/${username}`;
this.args.setScratchValue?.(null); this.args.setScratchValue?.(null);
model.hasValidated.pushObject(modelProperty); model.hasValidated.pushObject(modelProperty);

View File

@ -19,7 +19,7 @@ export default class MembersActivityEventTypeFilter extends Component {
get availableEventTypes() { get availableEventTypes() {
const extended = [...ALL_EVENT_TYPES]; const extended = [...ALL_EVENT_TYPES];
if (this.settings.get('commentsEnabled') !== 'off') { if (this.settings.commentsEnabled !== 'off') {
extended.push({event: 'comment_event', icon: 'event-comment', name: 'Comments'}); extended.push({event: 'comment_event', icon: 'event-comment', name: 'Comments'});
} }

View File

@ -178,12 +178,12 @@ export default class MembersFilter extends Component {
}); });
// exclude subscription filters if Stripe isn't connected // exclude subscription filters if Stripe isn't connected
if (!this.settings.get('paidMembersEnabled')) { if (!this.settings.paidMembersEnabled) {
availableFilters = availableFilters.reject(prop => prop.group === 'Subscription'); availableFilters = availableFilters.reject(prop => prop.group === 'Subscription');
} }
// exclude email filters if email functionality is disabled // exclude email filters if email functionality is disabled
if (this.settings.get('editorDefaultEmailRecipients') === 'disabled') { if (this.settings.editorDefaultEmailRecipients === 'disabled') {
availableFilters = availableFilters.reject(prop => prop.group === 'Email'); availableFilters = availableFilters.reject(prop => prop.group === 'Email');
} }
@ -253,7 +253,7 @@ export default class MembersFilter extends Component {
} else if (filterProperty.valueType === 'date') { } else if (filterProperty.valueType === 'date') {
let filterValue; let filterValue;
let tzMoment = moment.tz(moment(filter.value).format('YYYY-MM-DD'), this.settings.get('timezone')); let tzMoment = moment.tz(moment(filter.value).format('YYYY-MM-DD'), this.settings.timezone);
if (relationStr === '>') { if (relationStr === '>') {
tzMoment = tzMoment.set({hour: 23, minute: 59, second: 59}); tzMoment = tzMoment.set({hour: 23, minute: 59, second: 59});
@ -364,7 +364,7 @@ export default class MembersFilter extends Component {
relation, relation,
relationOptions: FILTER_RELATIONS_OPTIONS[key], relationOptions: FILTER_RELATIONS_OPTIONS[key],
value, value,
timezone: this.settings.get('timezone') timezone: this.settings.timezone
}); });
} }
} }
@ -429,7 +429,7 @@ export default class MembersFilter extends Component {
@action @action
handleSubmitKeyup(e) { handleSubmitKeyup(e) {
e.preventDefault(); e.preventDefault();
if (e.key === 'Enter') { if (e.key === 'Enter') {
this.applyFilter(); this.applyFilter();
} }
@ -465,7 +465,7 @@ export default class MembersFilter extends Component {
} }
if (newProp.valueType === 'date' && !defaultValue) { if (newProp.valueType === 'date' && !defaultValue) {
defaultValue = moment(moment.tz(this.settings.get('timezone')).format('YYYY-MM-DD')).toDate(); defaultValue = moment(moment.tz(this.settings.timezone).format('YYYY-MM-DD')).toDate();
} }
let defaultRelation = this.availableFilterRelationsOptions[newType][0].name; let defaultRelation = this.availableFilterRelationsOptions[newType][0].name;
@ -479,7 +479,7 @@ export default class MembersFilter extends Component {
relation: defaultRelation, relation: defaultRelation,
relationOptions: this.availableFilterRelationsOptions[newType], relationOptions: this.availableFilterRelationsOptions[newType],
value: defaultValue, value: defaultValue,
timezone: this.settings.get('timezone') timezone: this.settings.timezone
}); });
const filterToSwap = this.filters.find(f => f === filter); const filterToSwap = this.filters.find(f => f === filter);

View File

@ -14,22 +14,22 @@ export default class ModalEmailDesignSettings extends ModalComponent {
@service session; @service session;
@service settings; @service settings;
@tracked headerImage = this.settings.get('newsletterHeaderImage'); @tracked headerImage = this.settings.newsletterHeaderImage;
@tracked showHeaderIcon = this.settings.get('newsletterShowHeaderIcon'); @tracked showHeaderIcon = this.settings.newsletterShowHeaderIcon;
@tracked showHeaderTitle = this.settings.get('newsletterShowHeaderTitle'); @tracked showHeaderTitle = this.settings.newsletterShowHeaderTitle;
@tracked titleFontCategory = this.settings.get('newsletterTitleFontCategory'); @tracked titleFontCategory = this.settings.newsletterTitleFontCategory;
@tracked titleAlignment = this.settings.get('newsletterTitleAlignment'); @tracked titleAlignment = this.settings.newsletterTitleAlignment;
@tracked showFeatureImage = this.settings.get('newsletterShowFeatureImage'); @tracked showFeatureImage = this.settings.newsletterShowFeatureImage;
@tracked bodyFontCategory = this.settings.get('newsletterBodyFontCategory'); @tracked bodyFontCategory = this.settings.newsletterBodyFontCategory;
@tracked footerContent = this.settings.get('newsletterFooterContent'); @tracked footerContent = this.settings.newsletterFooterContent;
@tracked showBadge = this.settings.get('newsletterShowBadge'); @tracked showBadge = this.settings.newsletterShowBadge;
currentDate = moment().format('D MMM YYYY'); currentDate = moment().format('D MMM YYYY');
copyrightYear = new Date().getFullYear(); copyrightYear = new Date().getFullYear();
imageExtensions = IMAGE_EXTENSIONS; imageExtensions = IMAGE_EXTENSIONS;
get showHeader() { get showHeader() {
return (this.showHeaderIcon && this.settings.get('icon')) || this.showHeaderTitle; return (this.showHeaderIcon && this.settings.icon) || this.showHeaderTitle;
} }
get featureImageUrl() { get featureImageUrl() {
@ -77,15 +77,15 @@ export default class ModalEmailDesignSettings extends ModalComponent {
@task({drop: true}) @task({drop: true})
*saveSettings() { *saveSettings() {
this.settings.set('newsletterHeaderImage', this.headerImage); this.settings.newsletterHeaderImage = this.headerImage;
this.settings.set('newsletterShowHeaderIcon', this.showHeaderIcon); this.settings.newsletterShowHeaderIcon = this.showHeaderIcon;
this.settings.set('newsletterShowHeaderTitle', this.showHeaderTitle); this.settings.newsletterShowHeaderTitle = this.showHeaderTitle;
this.settings.set('newsletterTitleFontCategory', this.titleFontCategory); this.settings.newsletterTitleFontCategory = this.titleFontCategory;
this.settings.set('newsletterTitleAlignment', this.titleAlignment); this.settings.newsletterTitleAlignment = this.titleAlignment;
this.settings.set('newsletterShowFeatureImage', this.showFeatureImage); this.settings.newsletterShowFeatureImage = this.showFeatureImage;
this.settings.set('newsletterBodyFontCategory', this.bodyFontCategory); this.settings.newsletterBodyFontCategory = this.bodyFontCategory;
this.settings.set('newsletterFooterContent', this.footerContent); this.settings.newsletterFooterContent = this.footerContent;
this.settings.set('newsletterShowBadge', this.showBadge); this.settings.newsletterShowBadge = this.showBadge;
yield this.settings.save(); yield this.settings.save();
this.closeModal(); this.closeModal();

View File

@ -30,10 +30,10 @@ export default class ModalFreeMembershipSettings extends ModalBase {
this.close(); this.close();
}, },
updateName(value) { updateName(value) {
this.settings.set('membersFreePriceName', value); this.settings.membersFreePriceName = value;
}, },
updateDescription(value) { updateDescription(value) {
this.settings.set('membersFreePriceDescription', value); this.settings.membersFreePriceDescription = value;
}, },
setFreeSignupRedirect(url) { setFreeSignupRedirect(url) {
this.freeSignupRedirect = url; this.freeSignupRedirect = url;
@ -47,7 +47,7 @@ export default class ModalFreeMembershipSettings extends ModalBase {
*save() { *save() {
try { try {
this.send('validateFreeSignupRedirect'); this.send('validateFreeSignupRedirect');
if (this.settings.get('errors').length !== 0) { if (this.settings.errors.length !== 0) {
return; return;
} }
yield this.settings.save(); yield this.settings.save();
@ -61,12 +61,12 @@ export default class ModalFreeMembershipSettings extends ModalBase {
_validateSignupRedirect(url, type) { _validateSignupRedirect(url, type) {
let errMessage = `Please enter a valid URL`; let errMessage = `Please enter a valid URL`;
this.settings.get('errors').remove(type); this.settings.errors.remove(type);
this.settings.get('hasValidated').removeObject(type); this.settings.hasValidated.removeObject(type);
if (url === null) { if (url === null) {
this.settings.get('errors').add(type, errMessage); this.settings.errors.add(type, errMessage);
this.settings.get('hasValidated').pushObject(type); this.settings.hasValidated.pushObject(type);
return false; return false;
} }
@ -77,9 +77,9 @@ export default class ModalFreeMembershipSettings extends ModalBase {
if (url.href.startsWith(this.siteUrl)) { if (url.href.startsWith(this.siteUrl)) {
const path = url.href.replace(this.siteUrl, ''); const path = url.href.replace(this.siteUrl, '');
this.settings.set(type, path); this.settings.type = path;
} else { } else {
this.settings.set(type, url.href); this.settings.type = url.href;
} }
} }
} }

View File

@ -32,7 +32,7 @@ export default ModalComponent.extend({
confirm() {}, confirm() {},
backgroundStyle: computed('settings.accentColor', function () { backgroundStyle: computed('settings.accentColor', function () {
let color = this.settings.get('accentColor') || '#ffffff'; let color = this.settings.accentColor || '#ffffff';
return htmlSafe(`background-color: ${color}`); return htmlSafe(`background-color: ${color}`);
}), }),
@ -71,20 +71,20 @@ export default ModalComponent.extend({
selectedButtonStyle: computed('settings.portalButtonStyle', function () { selectedButtonStyle: computed('settings.portalButtonStyle', function () {
return this.buttonStyleOptions.find((buttonStyle) => { return this.buttonStyleOptions.find((buttonStyle) => {
return (buttonStyle.name === this.settings.get('portalButtonStyle')); return (buttonStyle.name === this.settings.portalButtonStyle);
}); });
}), }),
isFreeChecked: computed('settings.{portalPlans.[],membersSignupAccess}', function () { isFreeChecked: computed('settings.{portalPlans.[],membersSignupAccess}', function () {
const allowedPlans = this.settings.get('portalPlans') || []; const allowedPlans = this.settings.portalPlans || [];
return (this.settings.get('membersSignupAccess') === 'all' && allowedPlans.includes('free')); return (this.settings.membersSignupAccess === 'all' && allowedPlans.includes('free'));
}), }),
isMonthlyChecked: computed('settings.portalPlans.[]', 'membersUtils.paidMembersEnabled', function () { isMonthlyChecked: computed('settings.portalPlans.[]', 'membersUtils.paidMembersEnabled', function () {
const allowedPlans = this.settings.get('portalPlans') || []; const allowedPlans = this.settings.portalPlans || [];
return (this.membersUtils.paidMembersEnabled && allowedPlans.includes('monthly')); return (this.membersUtils.paidMembersEnabled && allowedPlans.includes('monthly'));
}), }),
isYearlyChecked: computed('settings.portalPlans.[]', 'membersUtils.paidMembersEnabled', function () { isYearlyChecked: computed('settings.portalPlans.[]', 'membersUtils.paidMembersEnabled', function () {
const allowedPlans = this.settings.get('portalPlans') || []; const allowedPlans = this.settings.portalPlans || [];
return (this.membersUtils.paidMembersEnabled && allowedPlans.includes('yearly')); return (this.membersUtils.paidMembersEnabled && allowedPlans.includes('yearly'));
}), }),
tiers: computed('model.tiers.[]', 'changedTiers.[]', 'isPreloading', function () { tiers: computed('model.tiers.[]', 'changedTiers.[]', 'isPreloading', function () {
@ -130,13 +130,13 @@ export default ModalComponent.extend({
}]; }];
this.iconExtensions = ICON_EXTENSIONS; this.iconExtensions = ICON_EXTENSIONS;
this.changedTiers = []; this.changedTiers = [];
this.set('supportAddress', this.parseEmailAddress(this.settings.get('membersSupportAddress'))); this.set('supportAddress', this.parseEmailAddress(this.settings.membersSupportAddress));
this.set('openSection', 'signup-options'); this.set('openSection', 'signup-options');
}, },
didInsertElement() { didInsertElement() {
this._super(...arguments); this._super(...arguments);
this.settings.get('errors').clear(); this.settings.errors.clear();
}, },
actions: { actions: {
@ -150,11 +150,11 @@ export default ModalComponent.extend({
this.updateAllowedTier(tierId, event.target.checked); this.updateAllowedTier(tierId, event.target.checked);
}, },
togglePortalButton(showButton) { togglePortalButton(showButton) {
this.settings.set('portalButton', showButton); this.settings.portalButton = showButton;
}, },
togglePortalName(showSignupName) { togglePortalName(showSignupName) {
this.settings.set('portalName', showSignupName); this.settings.portalName = showSignupName;
}, },
toggleSection(section) { toggleSection(section) {
if (this.get('openSection') === section) { if (this.get('openSection') === section) {
@ -169,7 +169,7 @@ export default ModalComponent.extend({
}, },
isPlanSelected(plan) { isPlanSelected(plan) {
const allowedPlans = this.settings.get('portalPlans'); const allowedPlans = this.settings.portalPlans;
return allowedPlans.includes(plan); return allowedPlans.includes(plan);
}, },
@ -191,11 +191,11 @@ export default ModalComponent.extend({
}, },
setButtonStyle(buttonStyle) { setButtonStyle(buttonStyle) {
this.settings.set('portalButtonStyle', buttonStyle.name); this.settings.portalButtonStyle = buttonStyle.name;
}, },
setSignupButtonText(event) { setSignupButtonText(event) {
this.settings.set('portalButtonSignupText', event.target.value); this.settings.portalButtonSignupText = event.target.value;
}, },
/** /**
* Fired after an image upload completes * Fired after an image upload completes
@ -206,7 +206,7 @@ export default ModalComponent.extend({
imageUploaded(property, results) { imageUploaded(property, results) {
if (results[0]) { if (results[0]) {
this.set('customIcon', results[0].url); this.set('customIcon', results[0].url);
this.settings.set('portalButtonIcon', results[0].url); this.settings.portalButtonIcon = results[0].url;
} }
}, },
/** /**
@ -221,11 +221,11 @@ export default ModalComponent.extend({
deleteCustomIcon() { deleteCustomIcon() {
this.set('customIcon', null); this.set('customIcon', null);
this.settings.set('portalButtonIcon', this.membersUtils.defaultIconKeys[0]); this.settings.portalButtonIcon = this.membersUtils.defaultIconKeys[0];
}, },
selectDefaultIcon(icon) { selectDefaultIcon(icon) {
this.settings.set('portalButtonIcon', icon); this.settings.portalButtonIcon = icon;
}, },
closeLeaveSettingsModal() { closeLeaveSettingsModal() {
@ -253,9 +253,9 @@ export default ModalComponent.extend({
this.set('supportAddress', supportAddress); this.set('supportAddress', supportAddress);
if (this.config.emailDomain && supportAddress === `noreply@${this.config.emailDomain}`) { if (this.config.emailDomain && supportAddress === `noreply@${this.config.emailDomain}`) {
this.settings.set('membersSupportAddress', 'noreply'); this.settings.membersSupportAddress = 'noreply';
} else { } else {
this.settings.set('membersSupportAddress', supportAddress); this.settings.membersSupportAddress = supportAddress;
} }
} }
}, },
@ -270,18 +270,18 @@ export default ModalComponent.extend({
}, },
updateAllowedPlan(plan, isChecked) { updateAllowedPlan(plan, isChecked) {
const portalPlans = this.settings.get('portalPlans') || []; const portalPlans = this.settings.portalPlans || [];
const allowedPlans = [...portalPlans]; const allowedPlans = [...portalPlans];
const freeTier = this.model.tiers.find(p => p.type === 'free'); const freeTier = this.model.tiers.find(p => p.type === 'free');
if (!isChecked) { if (!isChecked) {
this.settings.set('portalPlans', allowedPlans.filter(p => p !== plan)); this.settings.portalPlans = allowedPlans.filter(p => p !== plan);
if (plan === 'free') { if (plan === 'free') {
freeTier.set('visibility', 'none'); freeTier.set('visibility', 'none');
} }
} else { } else {
allowedPlans.push(plan); allowedPlans.push(plan);
this.settings.set('portalPlans', allowedPlans); this.settings.portalPlans = allowedPlans;
if (plan === 'free') { if (plan === 'free') {
freeTier.set('visibility', 'public'); freeTier.set('visibility', 'public');
} }
@ -303,12 +303,12 @@ export default ModalComponent.extend({
_validateSignupRedirect(url, type) { _validateSignupRedirect(url, type) {
let errMessage = `Please enter a valid URL`; let errMessage = `Please enter a valid URL`;
this.settings.get('errors').remove(type); this.settings.errors.remove(type);
this.settings.get('hasValidated').removeObject(type); this.settings.hasValidated.removeObject(type);
if (url === null) { if (url === null) {
this.settings.get('errors').add(type, errMessage); this.settings.errors.add(type, errMessage);
this.settings.get('hasValidated').pushObject(type); this.settings.hasValidated.pushObject(type);
return false; return false;
} }
@ -319,9 +319,9 @@ export default ModalComponent.extend({
if (url.href.startsWith(this.siteUrl)) { if (url.href.startsWith(this.siteUrl)) {
const path = url.href.replace(this.siteUrl, ''); const path = url.href.replace(this.siteUrl, '');
this.settings.set(type, path); this.settings[type] = path;
} else { } else {
this.settings.set(type, url.href); this.settings[type] = url.href;
} }
}, },
@ -330,9 +330,9 @@ export default ModalComponent.extend({
await this.model.preloadTask; await this.model.preloadTask;
} }
const portalButtonIcon = this.settings.get('portalButtonIcon') || ''; const portalButtonIcon = this.settings.portalButtonIcon || '';
if (portalButtonIcon && !this.membersUtils.defaultIconKeys.includes(portalButtonIcon)) { if (portalButtonIcon && !this.membersUtils.defaultIconKeys.includes(portalButtonIcon)) {
this.set('customIcon', this.settings.get('portalButtonIcon')); this.set('customIcon', this.settings.portalButtonIcon);
} }
this.siteUrl = this.config.get('blogUrl'); this.siteUrl = this.config.get('blogUrl');
@ -360,7 +360,7 @@ export default ModalComponent.extend({
this.settings.errors.remove('members_support_address'); this.settings.errors.remove('members_support_address');
this.settings.hasValidated.removeObject('members_support_address'); this.settings.hasValidated.removeObject('members_support_address');
if (this.settings.get('errors').length !== 0) { if (this.settings.errors.length !== 0) {
return; return;
} }
@ -374,14 +374,14 @@ export default ModalComponent.extend({
}) })
); );
const newEmail = this.settings.get('membersSupportAddress'); const newEmail = this.settings.membersSupportAddress;
try { try {
const result = yield this.settings.save(); const result = yield this.settings.save();
if (result._meta?.sent_email_verification) { if (result._meta?.sent_email_verification) {
yield this.modals.open(ConfirmEmailModal, { yield this.modals.open(ConfirmEmailModal, {
newEmail, newEmail,
currentEmail: this.settings.get('membersSupportAddress') currentEmail: this.settings.membersSupportAddress
}); });
} }

View File

@ -11,14 +11,14 @@ export default class ModalStripeConnect extends ModalBase {
@action @action
setStripeConnectIntegrationTokenSetting(stripeConnectIntegrationToken) { setStripeConnectIntegrationTokenSetting(stripeConnectIntegrationToken) {
this.settings.set('stripeConnectIntegrationToken', stripeConnectIntegrationToken); this.settings.stripeConnectIntegrationToken = stripeConnectIntegrationToken;
} }
@action @action
reset() { reset() {
// stripeConnectIntegrationToken is not a persisted value so we don't want // stripeConnectIntegrationToken is not a persisted value so we don't want
// to keep it around across transitions // to keep it around across transitions
this.settings.set('stripeConnectIntegrationToken', undefined); this.settings.stripeConnectIntegrationToken = undefined;
} }
@action @action
@ -36,7 +36,7 @@ export default class ModalStripeConnect extends ModalBase {
@action @action
updateSuccessModifier() { updateSuccessModifier() {
// Note, we should also check isStripeEnabled because stripeDirect option might be enabled // Note, we should also check isStripeEnabled because stripeDirect option might be enabled
if (this.membersUtils.get('isStripeEnabled') && this.settings.get('stripeConnectAccountId')) { if (this.membersUtils.get('isStripeEnabled') && this.settings.stripeConnectAccountId) {
if (this.modifier?.indexOf('stripe-connected') === -1) { if (this.modifier?.indexOf('stripe-connected') === -1) {
this.updateModifier(`${this.modifier} stripe-connected`); this.updateModifier(`${this.modifier} stripe-connected`);
} }
@ -49,7 +49,7 @@ export default class ModalStripeConnect extends ModalBase {
actions = { actions = {
confirm() { confirm() {
if (this.settings.get('stripeConnectAccountId')) { if (this.settings.stripeConnectAccountId) {
return this.confirmAction(); return this.confirmAction();
} }
// noop - enter key shouldn't do anything // noop - enter key shouldn't do anything

View File

@ -86,7 +86,7 @@ export default class ModalTierPrice extends ModalBase {
if (this.tier.get('trialDays')) { if (this.tier.get('trialDays')) {
this.freeTrialEnabled = true; this.freeTrialEnabled = true;
} }
this.accentColorStyle = htmlSafe(`color: ${this.settings.get('accentColor')}`); this.accentColorStyle = htmlSafe(`color: ${this.settings.accentColor}`);
} }
@action @action
@ -179,7 +179,7 @@ export default class ModalTierPrice extends ModalBase {
} }
this.tier.set('benefits', this.benefits.filter(benefit => !benefit.get('isBlank'))); this.tier.set('benefits', this.benefits.filter(benefit => !benefit.get('isBlank')));
try { try {
yield this.tier.save(); yield this.tier.save();
this.hasSaved = true; this.hasSaved = true;

View File

@ -8,7 +8,7 @@ export default class EditNewsletterPreview extends Component {
@service settings; @service settings;
get showHeader() { get showHeader() {
return (this.args.newsletter.showHeaderIcon && this.settings.get('icon')) return (this.args.newsletter.showHeaderIcon && this.settings.icon)
|| this.headerTitle; || this.headerTitle;
} }
@ -18,7 +18,7 @@ export default class EditNewsletterPreview extends Component {
get headerTitle() { get headerTitle() {
if (this.args.newsletter.showHeaderTitle) { if (this.args.newsletter.showHeaderTitle) {
return this.settings.get('title'); return this.settings.title;
} else if (this.args.newsletter.showHeaderName) { } else if (this.args.newsletter.showHeaderName) {
return this.args.newsletter.name; return this.args.newsletter.name;
} }

View File

@ -33,7 +33,7 @@ export default class VerifyEmail extends Component {
yield this.ajax.put(url, {data: {token}}); yield this.ajax.put(url, {data: {token}});
yield this.settings.reload(); yield this.settings.reload();
this.email = this.settings.get('membersSupportAddress'); this.email = this.settings.membersSupportAddress;
} catch (e) { } catch (e) {
this.error = e.message; this.error = e.message;
} }

View File

@ -27,7 +27,7 @@ export default class PostsListItemClicks extends Component {
let formattedTime = formatPostTime( let formattedTime = formatPostTime(
this.post.publishedAtUTC, this.post.publishedAtUTC,
{timezone: this.settings.get('timezone'), scheduled: true} {timezone: this.settings.timezone, scheduled: true}
); );
text.push(formattedTime); text.push(formattedTime);

View File

@ -8,7 +8,7 @@ export default class AccentColorFormField extends Component {
@service settings; @service settings;
get accentColor() { get accentColor() {
const color = this.settings.get('accentColor'); const color = this.settings.accentColor;
if (color && color[0] === '#') { if (color && color[0] === '#') {
return color.slice(1); return color.slice(1);
} }
@ -16,7 +16,7 @@ export default class AccentColorFormField extends Component {
} }
get accentColorPickerValue() { get accentColorPickerValue() {
return this.settings.get('accentColor') || '#ffffff'; return this.settings.accentColor || '#ffffff';
} }
get accentColorBgStyle() { get accentColorBgStyle() {
@ -31,7 +31,7 @@ export default class AccentColorFormField extends Component {
@action @action
async updateAccentColor(event) { async updateAccentColor(event) {
let newColor = event.target.value; let newColor = event.target.value;
const oldColor = this.settings.get('accentColor'); const oldColor = this.settings.accentColor;
// reset errors and validation // reset errors and validation
this.settings.errors.remove('accentColor'); this.settings.errors.remove('accentColor');
@ -62,7 +62,7 @@ export default class AccentColorFormField extends Component {
return; return;
} }
this.settings.set('accentColor', newColor); this.settings.accentColor = newColor;
this.args.didUpdate('accentColor', newColor); this.args.didUpdate('accentColor', newColor);
} else { } else {
this.settings.errors.add('accentColor', 'Please enter a color in hex format'); this.settings.errors.add('accentColor', 'Please enter a color in hex format');

View File

@ -25,7 +25,7 @@ export default class PublicationCoverFormField extends Component {
@action @action
update(value) { update(value) {
this.settings.set('coverImage', value); this.settings.coverImage = value;
this.args.didUpdate('coverImage', value); this.args.didUpdate('coverImage', value);
} }

View File

@ -21,7 +21,7 @@ export default class PublicationIconFormField extends Component {
@action @action
update(value) { update(value) {
this.settings.set('icon', value); this.settings.icon = value;
this.args.didUpdate('icon', value); this.args.didUpdate('icon', value);
} }
} }

View File

@ -21,7 +21,7 @@ export default class PublicationLogoFormField extends Component {
@action @action
update(value) { update(value) {
this.settings.set('logo', value); this.settings.logo = value;
this.args.didUpdate('logo', value); this.args.didUpdate('logo', value);
} }
} }

View File

@ -7,13 +7,13 @@ export default class SiteDescriptionFormField extends Component {
@action @action
update(event) { update(event) {
this.settings.set('description', event.target.value); this.settings.description = event.target.value;
} }
@action @action
async validate(event) { async validate(event) {
const value = event.target.value; const value = event.target.value;
this.settings.set('description', value); this.settings.description = value;
await this.settings.validate({property: 'description'}); await this.settings.validate({property: 'description'});
this.args.didUpdate('description', value); this.args.didUpdate('description', value);
} }

View File

@ -28,12 +28,12 @@ export default class SettingsMembersCommentAccess extends Component {
} }
get selectedOption() { get selectedOption() {
return this.options.find(o => o.value === this.settings.get('commentsEnabled')); return this.options.find(o => o.value === this.settings.commentsEnabled);
} }
@action @action
setCommentAccess(option) { setCommentAccess(option) {
this.settings.set('commentsEnabled', option.value); this.settings.commentsEnabled = option.value;
this.args.onChange?.(option.value); this.args.onChange?.(option.value);
} }
} }

View File

@ -37,22 +37,22 @@ export default class SettingsMembersDefaultPostAccess extends Component {
} }
get hasVisibilityFilter() { get hasVisibilityFilter() {
return !['public', 'members', 'paid'].includes(this.settings.get('defaultContentVisibility')); return !['public', 'members', 'paid'].includes(this.settings.defaultContentVisibility);
} }
get visibilityTiers() { get visibilityTiers() {
const visibilityTiersData = this.settings.get('defaultContentVisibilityTiers'); const visibilityTiersData = this.settings.defaultContentVisibilityTiers;
return (visibilityTiersData || []).map((id) => { return (visibilityTiersData || []).map((id) => {
return {id}; return {id};
}); });
} }
get selectedOption() { get selectedOption() {
if (this.settings.get('membersSignupAccess') === 'none') { if (this.settings.membersSignupAccess === 'none') {
return this.options.find(o => o.value === 'public'); return this.options.find(o => o.value === 'public');
} }
return this.options.find(o => o.value === this.settings.get('defaultContentVisibility')); return this.options.find(o => o.value === this.settings.defaultContentVisibility);
} }
@action @action
@ -61,21 +61,21 @@ export default class SettingsMembersDefaultPostAccess extends Component {
const tierIds = segment?.map((tier) => { const tierIds = segment?.map((tier) => {
return tier.id; return tier.id;
}); });
this.settings.set('defaultContentVisibility', 'tiers'); this.settings.defaultContentVisibility = 'tiers';
this.settings.set('defaultContentVisibilityTiers', tierIds); this.settings.defaultContentVisibilityTiers = tierIds;
this.showSegmentError = false; this.showSegmentError = false;
} else { } else {
this.settings.set('defaultContentVisibility', ''); this.settings.defaultContentVisibility = '';
this.showSegmentError = true; this.showSegmentError = true;
} }
} }
@action @action
setDefaultContentVisibility(option) { setDefaultContentVisibility(option) {
if (this.settings.get('membersSignupAccess') !== 'none') { if (this.settings.membersSignupAccess !== 'none') {
this.settings.set('defaultContentVisibility', option.value); this.settings.defaultContentVisibility = option.value;
if (option.value === 'tiers') { if (option.value === 'tiers') {
this.settings.set('defaultContentVisibilityTiers', []); this.settings.defaultContentVisibilityTiers = [];
} }
} }
} }

View File

@ -28,16 +28,16 @@ export default class SettingsMembersSubscriptionAccess extends Component {
} }
get selectedOption() { get selectedOption() {
return this.options.find(o => o.value === this.settings.get('membersSignupAccess')); return this.options.find(o => o.value === this.settings.membersSignupAccess);
} }
@action @action
setSignupAccess(option) { setSignupAccess(option) {
this.settings.set('membersSignupAccess', option.value); this.settings.membersSignupAccess = option.value;
this.args.onChange?.(option.value); this.args.onChange?.(option.value);
if (option.value === 'none') { if (option.value === 'none') {
this.settings.set('defaultContentVisibility', 'public'); this.settings.defaultContentVisibility = 'public';
} }
} }
} }

View File

@ -57,20 +57,20 @@ export default class StripeSettingsForm extends Component {
/** OLD **/ /** OLD **/
get stripeDirectPublicKey() { get stripeDirectPublicKey() {
return this.settings.get('stripePublishableKey'); return this.settings.stripePublishableKey;
} }
get stripeDirectSecretKey() { get stripeDirectSecretKey() {
return this.settings.settings.get('stripeSecretKey'); return this.settings.stripeSecretKey;
} }
get stripeConnectAccountId() { get stripeConnectAccountId() {
return this.settings.get('stripeConnectAccountId'); return this.settings.stripeConnectAccountId;
} }
get stripeConnectAccountName() { get stripeConnectAccountName() {
return this.settings.get('stripeConnectDisplayName'); return this.settings.stripeConnectDisplayName;
} }
get stripeConnectLivemode() { get stripeConnectLivemode() {
return this.settings.get('stripeConnectLivemode'); return this.settings.stripeConnectLivemode;
} }
get selectedCurrency() { get selectedCurrency() {
@ -79,7 +79,7 @@ export default class StripeSettingsForm extends Component {
} }
get stripePlans() { get stripePlans() {
const plans = this.settings.get('stripePlans'); const plans = this.settings.stripePlans;
const monthly = plans.find(plan => plan.interval === 'month'); const monthly = plans.find(plan => plan.interval === 'month');
const yearly = plans.find(plan => plan.interval === 'year' && plan.name !== 'Complimentary'); const yearly = plans.find(plan => plan.interval === 'year' && plan.name !== 'Complimentary');
@ -112,14 +112,14 @@ export default class StripeSettingsForm extends Component {
@action @action
setStripeDirectPublicKey(event) { setStripeDirectPublicKey(event) {
this.settings.set('stripeProductName', this.settings.get('title')); this.settings.stripeProductName = this.settings.title;
this.settings.set('stripePublishableKey', event.target.value); this.settings.stripePublishableKey = event.target.value;
} }
@action @action
setStripeDirectSecretKey(event) { setStripeDirectSecretKey(event) {
this.settings.set('stripeProductName', this.settings.get('title')); this.settings.stripeProductName = this.settings.title;
this.settings.set('stripeSecretKey', event.target.value); this.settings.stripeSecretKey = event.target.value;
} }
@action @action
@ -130,7 +130,7 @@ export default class StripeSettingsForm extends Component {
@action @action
setStripePlansCurrency(event) { setStripePlansCurrency(event) {
const newCurrency = event.value; const newCurrency = event.value;
const updatedPlans = this.settings.get('stripePlans').map((plan) => { const updatedPlans = this.settings.stripePlans.map((plan) => {
if (plan.name !== 'Complimentary') { if (plan.name !== 'Complimentary') {
return Object.assign({}, plan, { return Object.assign({}, plan, {
currency: newCurrency currency: newCurrency
@ -152,7 +152,7 @@ export default class StripeSettingsForm extends Component {
}); });
} }
this.settings.set('stripePlans', updatedPlans); this.settings.stripePlans = updatedPlans;
this._scratchStripeYearlyAmount = null; this._scratchStripeYearlyAmount = null;
this._scratchStripeMonthlyAmount = null; this._scratchStripeMonthlyAmount = null;
this.validateStripePlans(); this.validateStripePlans();
@ -160,7 +160,7 @@ export default class StripeSettingsForm extends Component {
@action @action
setStripeConnectIntegrationToken(event) { setStripeConnectIntegrationToken(event) {
this.settings.set('stripeProductName', this.settings.get('title')); this.settings.stripeProductName = this.settings.title;
this.args.setStripeConnectIntegrationTokenSetting(event.target.value); this.args.setStripeConnectIntegrationTokenSetting(event.target.value);
} }
@ -183,13 +183,13 @@ export default class StripeSettingsForm extends Component {
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.get('stripeDirect')
|| (this.membersUtils.isStripeEnabled && !this.settings.get('stripeConnectAccountId')); || (this.membersUtils.isStripeEnabled && !this.settings.stripeConnectAccountId);
} }
@action @action
validateStripePlans() { validateStripePlans() {
this.settings.get('errors').remove('stripePlans'); this.settings.errors.remove('stripePlans');
this.settings.get('hasValidated').removeObject('stripePlans'); this.settings.hasValidated.removeObject('stripePlans');
if (this._scratchStripeYearlyAmount === null) { if (this._scratchStripeYearlyAmount === null) {
this._scratchStripeYearlyAmount = this.stripePlans.yearly.amount; this._scratchStripeYearlyAmount = this.stripePlans.yearly.amount;
@ -203,7 +203,7 @@ export default class StripeSettingsForm extends Component {
const yearlyAmount = parseInt(this._scratchStripeYearlyAmount); const yearlyAmount = parseInt(this._scratchStripeYearlyAmount);
const monthlyAmount = parseInt(this._scratchStripeMonthlyAmount); const monthlyAmount = parseInt(this._scratchStripeMonthlyAmount);
if (!yearlyAmount || yearlyAmount < 1 || !monthlyAmount || monthlyAmount < 1) { if (!yearlyAmount || yearlyAmount < 1 || !monthlyAmount || monthlyAmount < 1) {
const minimum = Intl.NumberFormat(this.settings.get('locale'), { const minimum = Intl.NumberFormat(this.settings.locale, {
currency: selectedCurrency.isoCode, currency: selectedCurrency.isoCode,
style: 'currency' style: 'currency'
}).format(1); }).format(1);
@ -211,7 +211,7 @@ export default class StripeSettingsForm extends Component {
throw new TypeError(`Subscription amount must be at least ${minimum}`); throw new TypeError(`Subscription amount must be at least ${minimum}`);
} }
const updatedPlans = this.settings.get('stripePlans').map((plan) => { const updatedPlans = this.settings.stripePlans.map((plan) => {
if (plan.name !== 'Complimentary') { if (plan.name !== 'Complimentary') {
let newAmount; let newAmount;
if (plan.interval === 'year') { if (plan.interval === 'year') {
@ -226,11 +226,11 @@ export default class StripeSettingsForm extends Component {
return plan; return plan;
}); });
this.settings.set('stripePlans', updatedPlans); this.settings.stripePlans = updatedPlans;
} catch (err) { } catch (err) {
this.settings.get('errors').add('stripePlans', err.message); this.settings.errors.add('stripePlans', err.message);
} finally { } finally {
this.settings.get('hasValidated').pushObject('stripePlans'); this.settings.hasValidated.pushObject('stripePlans');
} }
} }
@ -296,12 +296,12 @@ export default class StripeSettingsForm extends Component {
*saveStripeSettingsTask() { *saveStripeSettingsTask() {
this.stripeConnectError = null; this.stripeConnectError = null;
if (this.settings.get('stripeConnectIntegrationToken')) { if (this.settings.stripeConnectIntegrationToken) {
try { try {
let response = yield this.settings.save(); let response = yield this.settings.save();
yield this.saveTier.perform(); yield this.saveTier.perform();
this.settings.set('portalPlans', ['free', 'monthly', 'yearly']); this.settings.portalPlans = ['free', 'monthly', 'yearly'];
response = yield this.settings.save(); response = yield this.settings.save();

View File

@ -20,46 +20,46 @@ export default class Newsletters extends Component {
mailgunRegions = [US, EU]; mailgunRegions = [US, EU];
get emailNewsletterEnabled() { get emailNewsletterEnabled() {
return this.settings.get('editorDefaultEmailRecipients') !== 'disabled'; return this.settings.editorDefaultEmailRecipients !== 'disabled';
} }
get mailgunRegion() { get mailgunRegion() {
if (!this.settings.get('mailgunBaseUrl')) { if (!this.settings.mailgunBaseUrl) {
return US; return US;
} }
return [US, EU].find((region) => { return [US, EU].find((region) => {
return region.baseUrl === this.settings.get('mailgunBaseUrl'); return region.baseUrl === this.settings.mailgunBaseUrl;
}); });
} }
get mailgunSettings() { get mailgunSettings() {
return { return {
apiKey: this.settings.get('mailgunApiKey') || '', apiKey: this.settings.mailgunApiKey || '',
domain: this.settings.get('mailgunDomain') || '', domain: this.settings.mailgunDomain || '',
baseUrl: this.settings.get('mailgunBaseUrl') || '' baseUrl: this.settings.mailgunBaseUrl || ''
}; };
} }
@action @action
setMailgunDomain(event) { setMailgunDomain(event) {
this.settings.set('mailgunDomain', event.target.value); this.settings.mailgunDomain = event.target.value;
if (!this.settings.get('mailgunBaseUrl')) { if (!this.settings.mailgunBaseUrl) {
this.settings.set('mailgunBaseUrl', this.mailgunRegion.baseUrl); this.settings.mailgunBaseUrl = this.mailgunRegion.baseUrl;
} }
} }
@action @action
setMailgunApiKey(event) { setMailgunApiKey(event) {
this.settings.set('mailgunApiKey', event.target.value); this.settings.mailgunApiKey = event.target.value;
if (!this.settings.get('mailgunBaseUrl')) { if (!this.settings.mailgunBaseUrl) {
this.settings.set('mailgunBaseUrl', this.mailgunRegion.baseUrl); this.settings.mailgunBaseUrl = this.mailgunRegion.baseUrl;
} }
} }
@action @action
setMailgunRegion(region) { setMailgunRegion(region) {
this.settings.set('mailgunBaseUrl', region.baseUrl); this.settings.mailgunBaseUrl = region.baseUrl;
} }
@action @action
@ -67,7 +67,7 @@ export default class Newsletters extends Component {
if (event) { if (event) {
event.preventDefault(); event.preventDefault();
} }
this.settings.set('emailTrackOpens', !this.settings.get('emailTrackOpens')); this.settings.emailTrackOpens = !this.settings.emailTrackOpens;
} }
@action @action
@ -75,7 +75,7 @@ export default class Newsletters extends Component {
if (event) { if (event) {
event.preventDefault(); event.preventDefault();
} }
this.settings.set('emailTrackClicks', !this.settings.get('emailTrackClicks')); this.settings.emailTrackClicks = !this.settings.emailTrackClicks;
} }
@action @action
@ -87,10 +87,10 @@ export default class Newsletters extends Component {
const newsletterEnabled = !this.emailNewsletterEnabled; const newsletterEnabled = !this.emailNewsletterEnabled;
if (newsletterEnabled) { if (newsletterEnabled) {
this.settings.set('editorDefaultEmailRecipients', 'visibility'); this.settings.editorDefaultEmailRecipients = 'visibility';
} else { } else {
this.settings.set('editorDefaultEmailRecipients', 'disabled'); this.settings.editorDefaultEmailRecipients = 'disabled';
this.settings.set('editorDefaultEmailRecipientsFilter', null); this.settings.editorDefaultEmailRecipientsFilter = null;
} }
this.recipientsSelectValue = this._getDerivedRecipientsSelectValue(); this.recipientsSelectValue = this._getDerivedRecipientsSelectValue();
@ -106,22 +106,22 @@ export default class Newsletters extends Component {
// Update the underlying setting properties to match the selected recipients option // Update the underlying setting properties to match the selected recipients option
if (['visibility', 'disabled'].includes(value)) { if (['visibility', 'disabled'].includes(value)) {
this.settings.set('editorDefaultEmailRecipients', value); this.settings.editorDefaultEmailRecipients = value;
this.settings.set('editorDefaultEmailRecipientsFilter', null); this.settings.editorDefaultEmailRecipientsFilter = null;
} else { } else {
this.settings.set('editorDefaultEmailRecipients', 'filter'); this.settings.editorDefaultEmailRecipients = 'filter';
} }
if (value === 'all-members') { if (value === 'all-members') {
this.settings.set('editorDefaultEmailRecipientsFilter', 'status:free,status:-free'); this.settings.editorDefaultEmailRecipientsFilter = 'status:free,status:-free';
} }
if (value === 'paid-only') { if (value === 'paid-only') {
this.settings.set('editorDefaultEmailRecipientsFilter', 'status:-free'); this.settings.editorDefaultEmailRecipientsFilter = 'status:-free';
} }
if (value === 'none') { if (value === 'none') {
this.settings.set('editorDefaultEmailRecipientsFilter', null); this.settings.editorDefaultEmailRecipientsFilter = null;
} }
// Update the value used to display the selected recipients option explicitly // Update the value used to display the selected recipients option explicitly
@ -131,12 +131,12 @@ export default class Newsletters extends Component {
@action @action
setDefaultEmailRecipientsFilter(filter) { setDefaultEmailRecipientsFilter(filter) {
this.settings.set('editorDefaultEmailRecipientsFilter', filter); this.settings.editorDefaultEmailRecipientsFilter = filter;
} }
_getDerivedRecipientsSelectValue() { _getDerivedRecipientsSelectValue() {
const defaultEmailRecipients = this.settings.get('editorDefaultEmailRecipients'); const defaultEmailRecipients = this.settings.editorDefaultEmailRecipients;
const defaultEmailRecipientsFilter = this.settings.get('editorDefaultEmailRecipientsFilter'); const defaultEmailRecipientsFilter = this.settings.editorDefaultEmailRecipientsFilter;
if (defaultEmailRecipients === 'filter') { if (defaultEmailRecipients === 'filter') {
if (defaultEmailRecipientsFilter === 'status:free,status:-free') { if (defaultEmailRecipientsFilter === 'status:free,status:-free') {

View File

@ -21,7 +21,7 @@ export default class TagForm extends Component {
@tracked codeInjectionOpen = false; @tracked codeInjectionOpen = false;
get seoTitle() { get seoTitle() {
const settingsTitle = this.settings.get('title') || ''; const settingsTitle = this.settings.title || '';
const tagName = settingsTitle ? `${this.args.tag.name} - ${settingsTitle}` : this.args.tag.name; const tagName = settingsTitle ? `${this.args.tag.name} - ${settingsTitle}` : this.args.tag.name;
let metaTitle = this.args.tag.metaTitle || tagName; let metaTitle = this.args.tag.metaTitle || tagName;
@ -74,7 +74,7 @@ export default class TagForm extends Component {
} }
get twitterDescription() { get twitterDescription() {
return this.args.tag.twitterDescription || this.seoDescription || this.settings.get('metaDescription') || ''; return this.args.tag.twitterDescription || this.seoDescription || this.settings.metaDescription || '';
} }
get twitterImage() { get twitterImage() {
@ -86,7 +86,7 @@ export default class TagForm extends Component {
} }
get facebookDescription() { get facebookDescription() {
return this.args.tag.facebookDescription || this.seoDescription || this.settings.get('metaDescription') || ''; return this.args.tag.facebookDescription || this.seoDescription || this.settings.metaDescription || '';
} }
get facebookImage() { get facebookImage() {

View File

@ -1039,7 +1039,7 @@ export default class EditorController extends Controller {
emailOnly, emailOnly,
newsletter newsletter
} = this.post; } = this.post;
let publishedAtBlogTZ = moment.tz(publishedAtUTC, this.settings.get('timezone')); let publishedAtBlogTZ = moment.tz(publishedAtUTC, this.settings.timezone);
let title = 'Scheduled'; let title = 'Scheduled';
let description = emailOnly ? ['Will be sent'] : ['Will be published']; let description = emailOnly ? ['Will be sent'] : ['Will be published'];

View File

@ -1035,7 +1035,7 @@ export default class LexicalEditorController extends Controller {
emailOnly, emailOnly,
newsletter newsletter
} = this.post; } = this.post;
let publishedAtBlogTZ = moment.tz(publishedAtUTC, this.settings.get('timezone')); let publishedAtBlogTZ = moment.tz(publishedAtUTC, this.settings.timezone);
let title = 'Scheduled'; let title = 'Scheduled';
let description = emailOnly ? ['Will be sent'] : ['Will be published']; let description = emailOnly ? ['Will be sent'] : ['Will be published'];

View File

@ -24,7 +24,7 @@ export default class MembersActivityController extends Controller {
hiddenEvents.push(...EMAIL_EVENTS); hiddenEvents.push(...EMAIL_EVENTS);
} }
if (this.settings.get('editorDefaultEmailRecipients') === 'disabled') { if (this.settings.editorDefaultEmailRecipients === 'disabled') {
hiddenEvents.push(...EMAIL_EVENTS, ...NEWSLETTER_EVENTS); hiddenEvents.push(...EMAIL_EVENTS, ...NEWSLETTER_EVENTS);
} }

View File

@ -35,7 +35,7 @@ export default class SettingsDesignIndexController extends Controller {
@task @task
*saveTask() { *saveTask() {
try { try {
if (this.settings.get('errors').length !== 0) { if (this.settings.errors.length !== 0) {
return; return;
} }

View File

@ -39,7 +39,7 @@ export default class GeneralController extends Controller {
@computed('config.blogUrl', 'settings.publicHash') @computed('config.blogUrl', 'settings.publicHash')
get privateRSSUrl() { get privateRSSUrl() {
let blogUrl = this.get('config.blogUrl'); let blogUrl = this.get('config.blogUrl');
let publicHash = this.get('settings.publicHash'); let publicHash = this.settings.publicHash;
return `${blogUrl}/${publicHash}/rss`; return `${blogUrl}/${publicHash}/rss`;
} }
@ -51,13 +51,13 @@ export default class GeneralController extends Controller {
@action @action
setTimezone(timezone) { setTimezone(timezone) {
this.set('settings.timezone', timezone.name); this.settings.timezone = timezone.name;
} }
@action @action
removeImage(image) { removeImage(image) {
// setting `null` here will error as the server treats it as "null" // setting `null` here will error as the server treats it as "null"
this.settings.set(image, ''); this.settings[image] = '';
} }
/** /**
@ -80,7 +80,7 @@ export default class GeneralController extends Controller {
@action @action
imageUploaded(property, results) { imageUploaded(property, results) {
if (results[0]) { if (results[0]) {
return this.settings.set(property, results[0].url); return this.settings[property] = results[0].url;
} }
} }
@ -88,18 +88,18 @@ export default class GeneralController extends Controller {
toggleIsPrivate(isPrivate) { toggleIsPrivate(isPrivate) {
let settings = this.settings; let settings = this.settings;
settings.set('isPrivate', isPrivate); settings.isPrivate = isPrivate;
settings.get('errors').remove('password'); settings.errors.remove('password');
let changedAttrs = settings.changedAttributes(); let changedAttrs = settings.changedAttributes();
// set a new random password when isPrivate is enabled // set a new random password when isPrivate is enabled
if (isPrivate && changedAttrs.isPrivate) { if (isPrivate && changedAttrs.isPrivate) {
settings.set('password', randomPassword()); settings.password = randomPassword();
// reset the password when isPrivate is disabled // reset the password when isPrivate is disabled
} else if (changedAttrs.password) { } else if (changedAttrs.password) {
settings.set('password', changedAttrs.password[0]); settings.password = changedAttrs.password[0];
} }
} }
@ -135,7 +135,7 @@ export default class GeneralController extends Controller {
this.clearScratchValues(); this.clearScratchValues();
config.set('blogTitle', settings.get('title')); config.set('blogTitle', settings.title);
if (changedAttrs.password) { if (changedAttrs.password) {
this.frontend.loginIfNeeded(); this.frontend.loginIfNeeded();

View File

@ -9,7 +9,7 @@ export default class AmpController extends Controller {
@action @action
update(value) { update(value) {
this.settings.set('amp', value); this.settings.amp = value;
} }
@action @action

View File

@ -9,7 +9,7 @@ export default class FirstpromoterController extends Controller {
@action @action
update(value) { update(value) {
this.settings.set('firstpromoter', value); this.settings.firstpromoter = value;
} }
@action @action

View File

@ -11,7 +11,7 @@ export default class SlackController extends Controller {
@service settings; @service settings;
get testNotificationDisabled() { get testNotificationDisabled() {
const slackUrl = this.settings.get('slackUrl'); const slackUrl = this.settings.slackUrl;
return !slackUrl; return !slackUrl;
} }

View File

@ -9,7 +9,7 @@ export default class UnsplashController extends Controller {
@action @action
update(value) { update(value) {
this.settings.set('unsplash', value); this.settings.unsplash = value;
} }
@action @action

View File

@ -119,7 +119,7 @@ export default class LabsController extends Controller {
// reload settings // reload settings
return this.settings.reload().then((settings) => { return this.settings.reload().then((settings) => {
this.feature.fetch(); this.feature.fetch();
this.config.set('blogTitle', settings.get('title')); this.config.set('blogTitle', settings.title);
}); });
}); });
}).catch((response) => { }).catch((response) => {

View File

@ -94,7 +94,7 @@ export default class MembersAccessController extends Controller {
} }
get isDirty() { get isDirty() {
return this.settings.get('hasDirtyAttributes') || this.hasChangedPrices; return this.settings.hasDirtyAttributes || this.hasChangedPrices;
} }
@action @action
@ -239,7 +239,7 @@ export default class MembersAccessController extends Controller {
// TODO: can these be worked out from settings in membersUtils? // TODO: can these be worked out from settings in membersUtils?
const monthlyPrice = Math.round(this.stripeMonthlyAmount * 100); const monthlyPrice = Math.round(this.stripeMonthlyAmount * 100);
const yearlyPrice = Math.round(this.stripeYearlyAmount * 100); const yearlyPrice = Math.round(this.stripeYearlyAmount * 100);
let portalPlans = this.settings.get('portalPlans') || []; let portalPlans = this.settings.portalPlans || [];
let isMonthlyChecked = portalPlans.includes('monthly'); let isMonthlyChecked = portalPlans.includes('monthly');
let isYearlyChecked = portalPlans.includes('yearly'); let isYearlyChecked = portalPlans.includes('yearly');
@ -339,11 +339,11 @@ export default class MembersAccessController extends Controller {
@task({drop: true}) @task({drop: true})
*saveSettingsTask(options) { *saveSettingsTask(options) {
if (this.settings.get('errors').length !== 0) { if (this.settings.errors.length !== 0) {
return; return;
} }
// When no filer is selected in `Specific tier(s)` option // When no filer is selected in `Specific tier(s)` option
if (!this.settings.get('defaultContentVisibility')) { if (!this.settings.defaultContentVisibility) {
return; return;
} }
const result = yield this.settings.save(); const result = yield this.settings.save();
@ -353,7 +353,7 @@ export default class MembersAccessController extends Controller {
} }
async saveTier() { async saveTier() {
const paidMembersEnabled = this.settings.get('paidMembersEnabled'); const paidMembersEnabled = this.settings.paidMembersEnabled;
if (this.tier && paidMembersEnabled) { if (this.tier && paidMembersEnabled) {
const monthlyAmount = Math.round(this.stripeMonthlyAmount * 100); const monthlyAmount = Math.round(this.stripeMonthlyAmount * 100);
const yearlyAmount = Math.round(this.stripeYearlyAmount * 100); const yearlyAmount = Math.round(this.stripeYearlyAmount * 100);
@ -385,12 +385,12 @@ export default class MembersAccessController extends Controller {
_validateSignupRedirect(url, type) { _validateSignupRedirect(url, type) {
const siteUrl = this.config.get('blogUrl'); const siteUrl = this.config.get('blogUrl');
let errMessage = `Please enter a valid URL`; let errMessage = `Please enter a valid URL`;
this.settings.get('errors').remove(type); this.settings.errors.remove(type);
this.settings.get('hasValidated').removeObject(type); this.settings.hasValidated.removeObject(type);
if (url === null) { if (url === null) {
this.settings.get('errors').add(type, errMessage); this.settings.errors.add(type, errMessage);
this.settings.get('hasValidated').pushObject(type); this.settings.hasValidated.pushObject(type);
return false; return false;
} }
@ -401,9 +401,9 @@ export default class MembersAccessController extends Controller {
if (url.href.startsWith(siteUrl)) { if (url.href.startsWith(siteUrl)) {
const path = url.href.replace(siteUrl, ''); const path = url.href.replace(siteUrl, '');
this.settings.set(type, path); this.settings[type] = path;
} else { } else {
this.settings.set(type, url.href); this.settings[type] = url.href;
} }
} }
} }

View File

@ -56,7 +56,7 @@ export default class NavigationController extends Controller {
return; return;
} }
let navItems = item.isSecondary ? this.get('settings.secondaryNavigation') : this.get('settings.navigation'); let navItems = item.isSecondary ? this.settings.secondaryNavigation : this.settings.navigation;
navItems.removeObject(item); navItems.removeObject(item);
this.set('dirtyAttributes', true); this.set('dirtyAttributes', true);
@ -95,7 +95,7 @@ export default class NavigationController extends Controller {
} }
addNewNavItem(item) { addNewNavItem(item) {
let navItems = item.isSecondary ? this.get('settings.secondaryNavigation') : this.get('settings.navigation'); let navItems = item.isSecondary ? this.settings.secondaryNavigation : this.settings.navigation;
item.set('isNew', false); item.set('isNew', false);
navItems.pushObject(item); navItems.pushObject(item);
@ -112,8 +112,8 @@ export default class NavigationController extends Controller {
@task @task
*saveTask() { *saveTask() {
let navItems = this.get('settings.navigation'); let navItems = this.settings.navigation;
let secondaryNavItems = this.get('settings.secondaryNavigation'); let secondaryNavItems = this.settings.secondaryNavigation;
let notifications = this.notifications; let notifications = this.notifications;
let validationPromises = []; let validationPromises = [];

View File

@ -170,7 +170,7 @@ export default class TierController extends Controller {
if (this.tier.get('errors').length !== 0) { if (this.tier.get('errors').length !== 0) {
return; return;
} }
if (this.settings.get('errors').length !== 0) { if (this.settings.errors.length !== 0) {
return; return;
} }
yield this.settings.save(); yield this.settings.save();
@ -183,12 +183,12 @@ export default class TierController extends Controller {
_validateSignupRedirect(url, type) { _validateSignupRedirect(url, type) {
let errMessage = `Please enter a valid URL`; let errMessage = `Please enter a valid URL`;
this.settings.get('errors').remove(type); this.settings.errors.remove(type);
this.settings.get('hasValidated').removeObject(type); this.settings.hasValidated.removeObject(type);
if (url === null) { if (url === null) {
this.settings.get('errors').add(type, errMessage); this.settings.errors.add(type, errMessage);
this.settings.get('hasValidated').pushObject(type); this.settings.hasValidated.pushObject(type);
return false; return false;
} }
@ -199,9 +199,9 @@ export default class TierController extends Controller {
if (url.href.startsWith(this.siteUrl)) { if (url.href.startsWith(this.siteUrl)) {
const path = url.href.replace(this.siteUrl, ''); const path = url.href.replace(this.siteUrl, '');
this.settings.set(type, path); this.settings[type] = path;
} else { } else {
this.settings.set(type, url.href); this.settings[type] = url.href;
} }
} }
} }

View File

@ -1,11 +1,10 @@
import Helper from '@ember/component/helper'; import Helper from '@ember/component/helper';
import {get} from '@ember/object';
import {inject as service} from '@ember/service'; import {inject as service} from '@ember/service';
export default class GetSetting extends Helper { export default class GetSetting extends Helper {
@service settings; @service settings;
compute([key = '']) { compute([key = '']) {
return get(this.settings, key); return this.settings[key];
} }
} }

View File

@ -59,6 +59,6 @@ export default class GhFormatPostTimeHelper extends Helper {
compute([timeago], options) { compute([timeago], options) {
assert('You must pass a time to the gh-format-post-time helper', timeago); assert('You must pass a time to the gh-format-post-time helper', timeago);
return formatPostTime(timeago, Object.assign({}, options, {timezone: this.settings.get('timezone')})); return formatPostTime(timeago, Object.assign({}, options, {timezone: this.settings.timezone}));
} }
} }

View File

@ -6,8 +6,8 @@ export default class IsMomentToday extends Helper {
@service settings; @service settings;
compute([date]) { compute([date]) {
const today = moment().tz(this.settings.get('timezone')); const today = moment().tz(this.settings.timezone);
const dateMoment = moment.tz(date, this.settings.get('timezone')); const dateMoment = moment.tz(date, this.settings.timezone);
return dateMoment.isSame(today, 'day'); return dateMoment.isSame(today, 'day');
} }

View File

@ -17,10 +17,10 @@ export default class MembersEventFilter extends Helper {
) { ) {
const excludedEventsSet = new Set(); const excludedEventsSet = new Set();
if (this.settings.get('editorDefaultEmailRecipients') === 'disabled') { if (this.settings.editorDefaultEmailRecipients === 'disabled') {
[...EMAIL_EVENTS, ...NEWSLETTER_EVENTS].forEach(type => excludedEventsSet.add(type)); [...EMAIL_EVENTS, ...NEWSLETTER_EVENTS].forEach(type => excludedEventsSet.add(type));
} }
if (this.settings.get('commentsEnabled') === 'off') { if (this.settings.commentsEnabled === 'off') {
excludedEventsSet.add('comment_event'); excludedEventsSet.add('comment_event');
} }

View File

@ -6,6 +6,6 @@ export default class MomentSiteTz extends Helper {
@service settings; @service settings;
compute([date]) { compute([date]) {
return moment.tz(date, this.settings.get('timezone')); return moment.tz(date, this.settings.timezone);
} }
} }

View File

@ -185,39 +185,39 @@ export default Model.extend(Comparable, ValidationEngine, {
hasBeenEmailed: computed('isPost', 'isSent', 'isPublished', 'email', function () { hasBeenEmailed: computed('isPost', 'isSent', 'isPublished', 'email', function () {
return this.isPost return this.isPost
&& (this.isSent || this.isPublished) && (this.isSent || this.isPublished)
&& this.email && this.email.status !== 'failed'; && this.email && this.email.status !== 'failed';
}), }),
didEmailFail: computed('isPost', 'isSent', 'isPublished', 'email.status', function () { didEmailFail: computed('isPost', 'isSent', 'isPublished', 'email.status', function () {
return this.isPost return this.isPost
&& (this.isSent || this.isPublished) && (this.isSent || this.isPublished)
&& this.email && this.email.status === 'failed'; && this.email && this.email.status === 'failed';
}), }),
showEmailOpenAnalytics: computed('hasBeenEmailed', 'isSent', 'isPublished', function () { showEmailOpenAnalytics: computed('hasBeenEmailed', 'isSent', 'isPublished', function () {
return this.hasBeenEmailed return this.hasBeenEmailed
&& !this.session.user.isContributor && !this.session.user.isContributor
&& this.settings.get('membersSignupAccess') !== 'none' && this.settings.membersSignupAccess !== 'none'
&& this.settings.get('editorDefaultEmailRecipients') !== 'disabled' && this.settings.editorDefaultEmailRecipients !== 'disabled'
&& this.hasBeenEmailed && this.hasBeenEmailed
&& this.email.trackOpens && this.email.trackOpens
&& this.settings.get('emailTrackOpens'); && this.settings.emailTrackOpens;
}), }),
showEmailClickAnalytics: computed('hasBeenEmailed', 'isSent', 'isPublished', 'email', function () { showEmailClickAnalytics: computed('hasBeenEmailed', 'isSent', 'isPublished', 'email', function () {
return this.hasBeenEmailed return this.hasBeenEmailed
&& !this.session.user.isContributor && !this.session.user.isContributor
&& this.settings.get('membersSignupAccess') !== 'none' && this.settings.membersSignupAccess !== 'none'
&& this.settings.get('editorDefaultEmailRecipients') !== 'disabled' && this.settings.editorDefaultEmailRecipients !== 'disabled'
&& (this.isSent || this.isPublished) && (this.isSent || this.isPublished)
&& this.email.trackClicks && this.email.trackClicks
&& this.settings.get('emailTrackClicks'); && this.settings.emailTrackClicks;
}), }),
showAttributionAnalytics: computed('isPage', 'emailOnly', 'isPublished', 'membersUtils.isMembersInviteOnly', function () { showAttributionAnalytics: computed('isPage', 'emailOnly', 'isPublished', 'membersUtils.isMembersInviteOnly', function () {
return (this.isPage || !this.emailOnly) return (this.isPage || !this.emailOnly)
&& this.isPublished && this.isPublished
&& this.feature.get('memberAttribution') && this.feature.get('memberAttribution')
&& !this.membersUtils.isMembersInviteOnly && !this.membersUtils.isMembersInviteOnly
&& !this.session.user.isContributor; && !this.session.user.isContributor;
@ -233,7 +233,7 @@ export default Model.extend(Comparable, ValidationEngine, {
|| this.showAttributionAnalytics || this.showAttributionAnalytics
); );
}), }),
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.get('config.blogUrl');
let uuid = this.uuid; let uuid = this.uuid;
@ -252,7 +252,7 @@ export default Model.extend(Comparable, ValidationEngine, {
visibilitySegment: computed('visibility', 'isPublic', 'tiers', function () { visibilitySegment: computed('visibility', 'isPublic', 'tiers', function () {
if (this.isPublic) { if (this.isPublic) {
return this.settings.get('defaultContentVisibility') === 'paid' ? 'status:-free' : 'status:free,status:-free'; return this.settings.defaultContentVisibility === 'paid' ? 'status:-free' : 'status:free,status:-free';
} else { } else {
if (this.visibility === 'members') { if (this.visibility === 'members') {
return 'status:free,status:-free'; return 'status:free,status:-free';
@ -321,7 +321,7 @@ export default Model.extend(Comparable, ValidationEngine, {
let publishedAtUTC = this.publishedAtUTC; let publishedAtUTC = this.publishedAtUTC;
let publishedAtBlogDate = this.publishedAtBlogDate; let publishedAtBlogDate = this.publishedAtBlogDate;
let publishedAtBlogTime = this.publishedAtBlogTime; let publishedAtBlogTime = this.publishedAtBlogTime;
let blogTimezone = this.get('settings.timezone'); let blogTimezone = this.settings.timezone;
if (!publishedAtUTC && isBlank(publishedAtBlogDate) && isBlank(publishedAtBlogTime)) { if (!publishedAtUTC && isBlank(publishedAtBlogDate) && isBlank(publishedAtBlogTime)) {
return null; return null;
@ -362,7 +362,7 @@ export default Model.extend(Comparable, ValidationEngine, {
_setPublishedAtBlogStrings(momentDate) { _setPublishedAtBlogStrings(momentDate) {
if (momentDate) { if (momentDate) {
let blogTimezone = this.get('settings.timezone'); let blogTimezone = this.settings.timezone;
let publishedAtBlog = moment.tz(momentDate, blogTimezone); let publishedAtBlog = moment.tz(momentDate, blogTimezone);
this.set('publishedAtBlogDate', publishedAtBlog.format('YYYY-MM-DD')); this.set('publishedAtBlogDate', publishedAtBlog.format('YYYY-MM-DD'));

View File

@ -1,7 +1,5 @@
/* eslint-disable camelcase */
import Model, {attr} from '@ember-data/model'; import Model, {attr} from '@ember-data/model';
import ValidationEngine from 'ghost-admin/mixins/validation-engine'; import ValidationEngine from 'ghost-admin/mixins/validation-engine';
import {and} from '@ember/object/computed';
export default Model.extend(ValidationEngine, { export default Model.extend(ValidationEngine, {
validationType: 'setting', validationType: 'setting',
@ -84,8 +82,6 @@ export default Model.extend(ValidationEngine, {
editorDefaultEmailRecipientsFilter: attr('members-segment-string'), editorDefaultEmailRecipientsFilter: attr('members-segment-string'),
emailVerificationRequired: attr('boolean'), emailVerificationRequired: attr('boolean'),
mailgunIsConfigured: and('mailgunApiKey', 'mailgunDomain', 'mailgunBaseUrl'),
// HACK - not a real model attribute but a workaround for Ember Data not // HACK - not a real model attribute but a workaround for Ember Data not
// exposing meta from save responses // exposing meta from save responses
_meta: attr() _meta: attr()

View File

@ -45,7 +45,7 @@ export default class CodeInjectionRoute extends AdminRoute {
async confirmUnsavedChanges() { async confirmUnsavedChanges() {
const settings = this.settings; const settings = this.settings;
if (settings.get('hasDirtyAttributes')) { if (settings.hasDirtyAttributes) {
this.confirmModal = this.modals this.confirmModal = this.modals
.open(ConfirmUnsavedChangesModal) .open(ConfirmUnsavedChangesModal)
.finally(() => { .finally(() => {

View File

@ -36,7 +36,7 @@ export default class SettingsDesignIndexRoute extends AuthenticatedRoute {
} }
confirmUnsavedChanges() { confirmUnsavedChanges() {
if (!this.settings.get('hasDirtyAttributes') && !this.customThemeSettings.isDirty) { if (!this.settings.hasDirtyAttributes && !this.customThemeSettings.isDirty) {
return Promise.resolve(true); return Promise.resolve(true);
} }

View File

@ -52,7 +52,7 @@ export default class GeneralSettingsRoute extends AdminRoute {
} }
async confirmUnsavedChanges() { async confirmUnsavedChanges() {
if (this.settings.get('hasDirtyAttributes')) { if (this.settings.hasDirtyAttributes) {
this.confirmModal = this.modals this.confirmModal = this.modals
.open(ConfirmUnsavedChangesModal) .open(ConfirmUnsavedChangesModal)
.finally(() => { .finally(() => {

View File

@ -43,7 +43,7 @@ export default class AMPRoute extends AdminRoute {
} }
async confirmUnsavedChanges() { async confirmUnsavedChanges() {
if (this.settings.get('hasDirtyAttributes')) { if (this.settings.hasDirtyAttributes) {
this.confirmModal = this.modals this.confirmModal = this.modals
.open(ConfirmUnsavedChangesModal) .open(ConfirmUnsavedChangesModal)
.finally(() => { .finally(() => {

View File

@ -43,7 +43,7 @@ export default class FirstPromotionIntegrationRoute extends AdminRoute {
} }
async confirmUnsavedChanges() { async confirmUnsavedChanges() {
if (this.settings.get('hasDirtyAttributes')) { if (this.settings.hasDirtyAttributes) {
this.confirmModal = this.modals this.confirmModal = this.modals
.open(ConfirmUnsavedChangesModal) .open(ConfirmUnsavedChangesModal)
.finally(() => { .finally(() => {

View File

@ -43,7 +43,7 @@ export default class SlackIntegrationRoute extends AdminRoute {
} }
async confirmUnsavedChanges() { async confirmUnsavedChanges() {
if (this.settings.get('hasDirtyAttributes')) { if (this.settings.hasDirtyAttributes) {
this.confirmModal = this.modals this.confirmModal = this.modals
.open(ConfirmUnsavedChangesModal) .open(ConfirmUnsavedChangesModal)
.finally(() => { .finally(() => {

View File

@ -43,7 +43,7 @@ export default class UnsplashIntegrationRoute extends AdminRoute {
} }
async confirmUnsavedChanges() { async confirmUnsavedChanges() {
if (this.settings.get('hasDirtyAttributes')) { if (this.settings.hasDirtyAttributes) {
this.confirmModal = this.modals this.confirmModal = this.modals
.open(ConfirmUnsavedChangesModal) .open(ConfirmUnsavedChangesModal)
.finally(() => { .finally(() => {

View File

@ -58,7 +58,7 @@ export default class MembersEmailLabsRoute extends AdminRoute {
} }
confirmUnsavedChanges() { confirmUnsavedChanges() {
if (!this.settings.get('hasDirtyAttributes')) { if (!this.settings.hasDirtyAttributes) {
return Promise.resolve(true); return Promise.resolve(true);
} }

View File

@ -3,9 +3,7 @@ import EmberObject, {action} from '@ember/object';
import Service, {inject as service} from '@ember/service'; import Service, {inject as service} from '@ember/service';
import ValidationEngine from 'ghost-admin/mixins/validation-engine'; import ValidationEngine from 'ghost-admin/mixins/validation-engine';
import {isArray} from '@ember/array'; import {isArray} from '@ember/array';
import {observes} from '@ember-decorators/object';
import {task} from 'ember-concurrency'; import {task} from 'ember-concurrency';
import {tracked} from '@glimmer/tracking';
const VIEW_COLORS = [ const VIEW_COLORS = [
'midgrey', 'midgrey',
@ -108,33 +106,23 @@ export default class CustomViewsService extends Service {
@service session; @service session;
@service settings; @service settings;
@tracked viewList = []; get viewList() {
constructor() {
super(...arguments);
this.updateViewList();
}
// eslint-disable-next-line ghost/ember/no-observers
@observes('settings.sharedViews', 'session.{isAuthenticated,user}')
async updateViewList() {
let {settings, session} = this; let {settings, session} = this;
// avoid fetching user before authenticated otherwise the 403 can fire // avoid fetching user before authenticated otherwise the 403 can fire
// during authentication and cause errors during setup/signin // during authentication and cause errors during setup/signin
if (!session.isAuthenticated || !session.user) { if (!session.isAuthenticated || !session.user) {
return; return [];
} }
let views = JSON.parse(settings.get('sharedViews') || '[]'); let views = JSON.parse(settings.sharedViews || '[]');
views = isArray(views) ? views : []; views = isArray(views) ? views : [];
let viewList = []; const viewList = [];
// contributors can only see their own draft posts so it doesn't make // contributors can only see their own draft posts so it doesn't make
// sense to show them default views which change the status/type filter // sense to show them default views which change the status/type filter
let user = await session.user; if (!session.user.isContributor) {
if (!user.isContributor) {
viewList.push(...DEFAULT_VIEWS); viewList.push(...DEFAULT_VIEWS);
} }
@ -142,15 +130,17 @@ export default class CustomViewsService extends Service {
return CustomView.create(view); return CustomView.create(view);
})); }));
this.viewList = viewList; return viewList;
} }
@task @task
*saveViewTask(view) { *saveViewTask(view) {
yield view.validate(); yield view.validate();
const {viewList} = this;
// perform some ad-hoc validation of duplicate names because ValidationEngine doesn't support it // perform some ad-hoc validation of duplicate names because ValidationEngine doesn't support it
let duplicateView = this.viewList.find((existingView) => { let duplicateView = viewList.find((existingView) => {
return existingView.route === view.route return existingView.route === view.route
&& existingView.name.trim().toLowerCase() === view.name.trim().toLowerCase() && existingView.name.trim().toLowerCase() === view.name.trim().toLowerCase()
&& !isFilterEqual(existingView.filter, view.filter); && !isFilterEqual(existingView.filter, view.filter);
@ -165,15 +155,15 @@ export default class CustomViewsService extends Service {
// remove an older version of the view from our views list // remove an older version of the view from our views list
// - we don't allow editing the filter and route+filter combos are unique // - we don't allow editing the filter and route+filter combos are unique
// - we create a new instance of a view from an existing one when editing to act as a "scratch" view // - we create a new instance of a view from an existing one when editing to act as a "scratch" view
let matchingView = this.viewList.find(existingView => isViewEqual(existingView, view)); let matchingView = viewList.find(existingView => isViewEqual(existingView, view));
if (matchingView) { if (matchingView) {
this.viewList.replace(this.viewList.indexOf(matchingView), 1, [view]); viewList.replace(viewList.indexOf(matchingView), 1, [view]);
} else { } else {
this.viewList.push(view); viewList.push(view);
} }
// rebuild the "views" array in our user settings json string // rebuild the "views" array in our user settings json string
yield this._saveViewSettings(); yield this._saveViewSettings(viewList);
view.set('isNew', false); view.set('isNew', false);
return view; return view;
@ -181,10 +171,11 @@ export default class CustomViewsService extends Service {
@task @task
*deleteViewTask(view) { *deleteViewTask(view) {
let matchingView = this.viewList.find(existingView => isViewEqual(existingView, view)); const {viewList} = this;
let matchingView = viewList.find(existingView => isViewEqual(existingView, view));
if (matchingView && !matchingView.isDefault) { if (matchingView && !matchingView.isDefault) {
this.viewList.removeObject(matchingView); viewList.removeObject(matchingView);
yield this._saveViewSettings(); yield this._saveViewSettings(viewList);
return true; return true;
} }
} }
@ -234,9 +225,9 @@ export default class CustomViewsService extends Service {
}); });
} }
async _saveViewSettings() { async _saveViewSettings(viewList) {
let sharedViews = this.viewList.reject(view => view.isDefault).map(view => view.toJSON()); let sharedViews = viewList.reject(view => view.isDefault).map(view => view.toJSON());
this.settings.set('sharedViews', JSON.stringify(sharedViews)); this.settings.sharedViews = JSON.stringify(sharedViews);
return this.settings.save(); return this.settings.save();
} }
} }

View File

@ -366,7 +366,7 @@ export default class DashboardStatsService extends Service {
this.siteStatus = { this.siteStatus = {
hasPaidTiers, hasPaidTiers,
hasMultipleTiers: hasPaidTiers && this.activePaidTiers.length > 1, hasMultipleTiers: hasPaidTiers && this.activePaidTiers.length > 1,
newslettersEnabled: this.settings.get('editorDefaultEmailRecipients') !== 'disabled', newslettersEnabled: this.settings.editorDefaultEmailRecipients !== 'disabled',
membersEnabled: this.membersUtils.isMembersEnabled membersEnabled: this.membersUtils.isMembersEnabled
}; };
} }

View File

@ -71,7 +71,7 @@ export default class FeatureService extends Service {
@computed('settings.labs') @computed('settings.labs')
get labs() { get labs() {
let labs = this.get('settings.labs'); let labs = this.settings.labs;
try { try {
return JSON.parse(labs) || {}; return JSON.parse(labs) || {};

View File

@ -11,7 +11,7 @@ export default class FrontendService extends Service {
_lastPassword = null; _lastPassword = null;
get hasPasswordChanged() { get hasPasswordChanged() {
return this._lastPassword !== this.settings.get('password'); return this._lastPassword !== this.settings.password;
} }
getUrl(path) { getUrl(path) {
@ -23,9 +23,9 @@ export default class FrontendService extends Service {
} }
async loginIfNeeded() { async loginIfNeeded() {
if (this.settings.get('isPrivate') && (this.hasPasswordChanged || !this._hasLoggedIn)) { if (this.settings.isPrivate && (this.hasPasswordChanged || !this._hasLoggedIn)) {
const privateLoginUrl = this.getUrl('/private/?r=%2F'); const privateLoginUrl = this.getUrl('/private/?r=%2F');
this._lastPassword = this.settings.get('password'); this._lastPassword = this.settings.password;
return fetch(privateLoginUrl, { return fetch(privateLoginUrl, {
method: 'POST', method: 'POST',

View File

@ -7,15 +7,15 @@ export default class MembersUtilsService extends Service {
@service store; @service store;
get isMembersEnabled() { get isMembersEnabled() {
return this.settings.get('membersEnabled'); return this.settings.membersEnabled;
} }
get paidMembersEnabled() { get paidMembersEnabled() {
return this.settings.get('paidMembersEnabled'); return this.settings.paidMembersEnabled;
} }
get isMembersInviteOnly() { get isMembersInviteOnly() {
return this.settings.get('membersInviteOnly'); return this.settings.membersInviteOnly;
} }
/** /**
@ -24,8 +24,8 @@ export default class MembersUtilsService extends Service {
get isStripeEnabled() { get isStripeEnabled() {
const stripeDirect = this.config.get('stripeDirect'); const stripeDirect = this.config.get('stripeDirect');
const hasDirectKeys = !!this.settings.get('stripeSecretKey') && !!this.settings.get('stripePublishableKey'); const hasDirectKeys = !!this.settings.stripeSecretKey && !!this.settings.stripePublishableKey;
const hasConnectKeys = !!this.settings.get('stripeConnectSecretKey') && !!this.settings.get('stripeConnectPublishableKey'); const hasConnectKeys = !!this.settings.stripeConnectSecretKey && !!this.settings.stripeConnectPublishableKey;
if (stripeDirect) { if (stripeDirect) {
return hasDirectKeys; return hasDirectKeys;
@ -66,23 +66,23 @@ export default class MembersUtilsService extends Service {
} }
get buttonIcon() { get buttonIcon() {
return this.settings.get('portalButtonIcon') || this.defaultIconKeys[0]; return this.settings.portalButtonIcon || this.defaultIconKeys[0];
} }
// Plan helpers ------------------------------------------------------------ // Plan helpers ------------------------------------------------------------
get isFreeChecked() { get isFreeChecked() {
const allowedPlans = this.settings.get('portalPlans') || []; const allowedPlans = this.settings.portalPlans || [];
return !!(this.settings.get('membersSignupAccess') === 'all' && allowedPlans.includes('free')); return !!(this.settings.membersSignupAccess === 'all' && allowedPlans.includes('free'));
} }
get isMonthlyChecked() { get isMonthlyChecked() {
const allowedPlans = this.settings.get('portalPlans') || []; const allowedPlans = this.settings.portalPlans || [];
return !!(this.isStripeConfigured && allowedPlans.includes('monthly')); return !!(this.isStripeConfigured && allowedPlans.includes('monthly'));
} }
get isYearlyChecked() { get isYearlyChecked() {
const allowedPlans = this.settings.get('portalPlans') || []; const allowedPlans = this.settings.portalPlans || [];
return !!(this.isStripeConfigured && allowedPlans.includes('yearly')); return !!(this.isStripeConfigured && allowedPlans.includes('yearly'));
} }
@ -92,17 +92,17 @@ export default class MembersUtilsService extends Service {
let { let {
disableBackground = false, disableBackground = false,
page = 'signup', page = 'signup',
button = this.settings.get('portalButton'), button = this.settings.portalButton,
buttonIcon = this.buttonIcon, buttonIcon = this.buttonIcon,
isFreeChecked = this.isFreeChecked, isFreeChecked = this.isFreeChecked,
isMonthlyChecked = this.isMonthlyChecked, isMonthlyChecked = this.isMonthlyChecked,
isYearlyChecked = this.isYearlyChecked, isYearlyChecked = this.isYearlyChecked,
monthlyPrice, monthlyPrice,
yearlyPrice, yearlyPrice,
portalPlans = this.settings.get('portalPlans'), portalPlans = this.settings.portalPlans,
portalTiers, portalTiers,
currency, currency,
membersSignupAccess = this.settings.get('membersSignupAccess') membersSignupAccess = this.settings.membersSignupAccess
} = overrides; } = overrides;
const tiers = this.store.peekAll('tier') || []; const tiers = this.store.peekAll('tier') || [];
@ -114,11 +114,11 @@ export default class MembersUtilsService extends Service {
const baseUrl = this.config.get('blogUrl'); const baseUrl = this.config.get('blogUrl');
const portalBase = '/#/portal/preview'; const portalBase = '/#/portal/preview';
const settingsParam = new URLSearchParams(); const settingsParam = new URLSearchParams();
const signupButtonText = this.settings.get('portalButtonSignupText') || ''; const signupButtonText = this.settings.portalButtonSignupText || '';
const allowSelfSignup = membersSignupAccess === 'all' && (!this.isStripeEnabled || isFreeChecked); const allowSelfSignup = membersSignupAccess === 'all' && (!this.isStripeEnabled || isFreeChecked);
settingsParam.append('button', button); settingsParam.append('button', button);
settingsParam.append('name', this.settings.get('portalName')); settingsParam.append('name', this.settings.portalName);
settingsParam.append('isFree', isFreeChecked); settingsParam.append('isFree', isFreeChecked);
settingsParam.append('isMonthly', isMonthlyChecked); settingsParam.append('isMonthly', isMonthlyChecked);
settingsParam.append('isYearly', isYearlyChecked); settingsParam.append('isYearly', isYearlyChecked);
@ -136,11 +136,11 @@ export default class MembersUtilsService extends Service {
settingsParam.append('portalProducts', encodeURIComponent(portalTiers)); settingsParam.append('portalProducts', encodeURIComponent(portalTiers));
} }
if (this.settings.get('accentColor') === '' || this.settings.get('accentColor')) { if (this.settings.accentColor === '' || this.settings.accentColor) {
settingsParam.append('accentColor', encodeURIComponent(`${this.settings.get('accentColor')}`)); settingsParam.append('accentColor', encodeURIComponent(`${this.settings.accentColor}`));
} }
if (this.settings.get('portalButtonStyle')) { if (this.settings.portalButtonStyle) {
settingsParam.append('buttonStyle', encodeURIComponent(this.settings.get('portalButtonStyle'))); settingsParam.append('buttonStyle', encodeURIComponent(this.settings.portalButtonStyle));
} }
if (monthlyPrice) { if (monthlyPrice) {

View File

@ -1,5 +1,5 @@
import Service, {inject as service} from '@ember/service'; import Service, {inject as service} from '@ember/service';
import {action, set} from '@ember/object'; import {action} from '@ember/object';
import {observes} from '@ember-decorators/object'; import {observes} from '@ember-decorators/object';
import {tracked} from '@glimmer/tracking'; import {tracked} from '@glimmer/tracking';
@ -39,10 +39,7 @@ export default class NavigationService extends Service {
this.settings.expanded = {}; this.settings.expanded = {};
} }
// set is still needed here because we're not tracking deep keys this.settings.expanded[key] = !this.settings.expanded[key];
// and Ember picks up that our templates are dependent on them and
// complains. TODO: can we avoid set?
set(this.settings.expanded, key, !this.settings.expanded[key]);
return await this._saveNavigationSettings(); return await this._saveNavigationSettings();
} }

View File

@ -1,27 +1,28 @@
import Ember from 'ember';
import RSVP from 'rsvp';
import Service, {inject as service} from '@ember/service'; import Service, {inject as service} from '@ember/service';
import ValidationEngine from 'ghost-admin/mixins/validation-engine'; import ValidationEngine from 'ghost-admin/mixins/validation-engine';
import classic from 'ember-classic-decorator'; import {tracked} from '@glimmer/tracking';
import {get} from '@ember/object';
// ember-cli-shims doesn't export _ProxyMixin export default class SettingsService extends Service.extend(ValidationEngine) {
const {_ProxyMixin} = Ember;
@classic
export default class SettingsService extends Service.extend(_ProxyMixin, ValidationEngine) {
@service store; @service store;
// will be set to the single Settings model, it's a reference so any later // will be set to the single Settings model, it's a reference so any later
// changes to the settings object in the store will be reflected // changes to the settings object in the store will be reflected
content = null; settingsModel = null;
validationType = 'setting'; validationType = 'setting';
_loadingPromise = null; _loadingPromise = null;
// this is an odd case where we only want to react to changes that we get // this is an odd case where we only want to react to changes that we get
// back from the API rather than local updates // back from the API rather than local updates
settledIcon = ''; @tracked settledIcon = '';
get hasDirtyAttributes() {
return this.settingsModel?.hasDirtyAttributes || false;
}
get mailgunIsConfigured() {
return this.mailgunApiKey && this.mailgunDomain && this.mailgunBaseUrl;
}
// the settings API endpoint is a little weird as it's singular and we have // the settings API endpoint is a little weird as it's singular and we have
// to pass in all types - if we ever fetch settings without all types then // to pass in all types - if we ever fetch settings without all types then
@ -39,40 +40,56 @@ export default class SettingsService extends Service.extend(_ProxyMixin, Validat
return this._loadingPromise; return this._loadingPromise;
} }
fetch() { async fetch() {
if (!this.content) { if (!this.settingsModel) {
return this.reload(); return this.reload();
} else { } else {
return RSVP.resolve(this); return this;
} }
} }
reload() { async reload() {
return this._loadSettings().then((settings) => { const settingsModel = await this._loadSettings();
this.set('content', settings);
this.set('settledIcon', get(settings, 'icon')); this.settingsModel = settingsModel;
return this; this.settledIcon = settingsModel.icon;
settingsModel.eachAttribute((name) => {
if (!Object.prototype.hasOwnProperty.call(this, name)) {
Object.defineProperty(this, name, {
get() {
return this.settingsModel[name];
},
set(newValue) {
this.settingsModel[name] = newValue;
}
});
}
}); });
return this;
} }
async save() { async save() {
let settings = this.content; const {settingsModel} = this;
if (!settings) { if (!settingsModel) {
return false; return false;
} }
await settings.save(); await settingsModel.save();
await this.validate(); await this.validate();
this.set('settledIcon', settings.icon);
return settings; this.settledIcon = settingsModel.icon;
return this;
} }
rollbackAttributes() { rollbackAttributes() {
return this.content?.rollbackAttributes(); return this.settingsModel?.rollbackAttributes();
} }
changedAttributes() { changedAttributes() {
return this.content?.changedAttributes(); return this.settingsModel?.changedAttributes();
} }
} }

View File

@ -193,11 +193,11 @@ export default class ThemeManagementService extends Service {
get previewData() { get previewData() {
const params = new URLSearchParams(); const params = new URLSearchParams();
params.append('c', this.settings.get('accentColor') || '#ffffff'); params.append('c', this.settings.accentColor || '#ffffff');
params.append('d', this.settings.get('description')); params.append('d', this.settings.description);
params.append('icon', this.settings.get('icon')); params.append('icon', this.settings.icon);
params.append('logo', this.settings.get('logo')); params.append('logo', this.settings.logo);
params.append('cover', this.settings.get('coverImage')); params.append('cover', this.settings.coverImage);
params.append('custom', JSON.stringify(this.customThemeSettings.keyValueObject)); params.append('custom', JSON.stringify(this.customThemeSettings.keyValueObject));

View File

@ -70,7 +70,7 @@ export default class UiService extends Service {
} }
get adjustedAccentColor() { get adjustedAccentColor() {
const accentColor = Color(this.settings.get('accentColor')); const accentColor = Color(this.settings.accentColor);
const backgroundColor = Color(this.backgroundColor); const backgroundColor = Color(this.backgroundColor);
// WCAG contrast. 1 = lowest contrast, 21 = highest contrast // WCAG contrast. 1 = lowest contrast, 21 = highest contrast

View File

@ -113,8 +113,8 @@ export default class PublishOptions {
} }
get emailDisabledInSettings() { get emailDisabledInSettings() {
return get(this.settings, 'editorDefaultEmailRecipients') === 'disabled' return this.settings.editorDefaultEmailRecipients === 'disabled'
|| get(this.settings, 'membersSignupAccess') === 'none'; || this.settings.membersSignupAccess === 'none';
} }
// publish type dropdown is not shown at all // publish type dropdown is not shown at all
@ -130,7 +130,7 @@ export default class PublishOptions {
} }
get mailgunIsConfigured() { get mailgunIsConfigured() {
return get(this.settings, 'mailgunIsConfigured') return this.settings.mailgunIsConfigured
|| get(this.config, 'mailgunIsConfigured'); || get(this.config, 'mailgunIsConfigured');
} }
@ -168,8 +168,8 @@ export default class PublishOptions {
} }
get defaultRecipientFilter() { get defaultRecipientFilter() {
const recipients = this.settings.get('editorDefaultEmailRecipients'); const recipients = this.settings.editorDefaultEmailRecipients;
const filter = this.settings.get('editorDefaultEmailRecipientsFilter'); const filter = this.settings.editorDefaultEmailRecipientsFilter;
const usuallyNobody = recipients === 'filter' && filter === null; const usuallyNobody = recipients === 'filter' && filter === null;
@ -254,8 +254,8 @@ export default class PublishOptions {
// Set publish type to "Publish" but keep email recipients matching post visibility // Set publish type to "Publish" but keep email recipients matching post visibility
// to avoid multiple clicks to turn on emailing // to avoid multiple clicks to turn on emailing
if ( if (
this.settings.get('editorDefaultEmailRecipients') === 'filter' && this.settings.editorDefaultEmailRecipients === 'filter' &&
this.settings.get('editorDefaultEmailRecipientsFilter') === null this.settings.editorDefaultEmailRecipientsFilter === null
) { ) {
this.publishType = 'publish'; this.publishType = 'publish';
} }
@ -382,7 +382,7 @@ export default class PublishOptions {
try { try {
if (this.limit.limiter && this.limit.limiter.isLimited('emails')) { if (this.limit.limiter && this.limit.limiter.isLimited('emails')) {
await this.limit.limiter.errorIfWouldGoOverLimit('emails'); await this.limit.limiter.errorIfWouldGoOverLimit('emails');
} else if (get(this.settings, 'emailVerificationRequired')) { } else if (this.settings.emailVerificationRequired) {
this.emailDisabledError = 'Email sending is temporarily disabled because your account is currently in review. You should have an email about this from us already, but you can also reach us any time at support@ghost.org.'; this.emailDisabledError = 'Email sending is temporarily disabled because your account is currently in review. You should have an email about this from us already, but you can also reach us any time at support@ghost.org.';
} }
} catch (e) { } catch (e) {

View File

@ -16,8 +16,8 @@ describe('Integration: Helper: gh-format-post-time', function () {
beforeEach(function () { beforeEach(function () {
let settings = this.owner.lookup('service:settings'); let settings = this.owner.lookup('service:settings');
settings.content = {}; settings.settingsModel = {};
settings.set('timezone', timezoneForTest); settings.timezone = timezoneForTest;
}); });
afterEach(function () { afterEach(function () {

View File

@ -50,13 +50,13 @@ describe.skip('Unit: Controller: settings/design', function () {
NavItem.create({label: 'Third', url: ''}) NavItem.create({label: 'Third', url: ''})
]})); ]}));
// blank item won't get added because the last item is incomplete // blank item won't get added because the last item is incomplete
expect(ctrl.get('settings.navigation.length')).to.equal(3); expect(ctrl.settings.navigation.length).to.equal(3);
ctrl.get('save').perform().then(function passedValidation() { ctrl.get('save').perform().then(function passedValidation() {
assert(false, 'navigationItems weren\'t validated on save'); assert(false, 'navigationItems weren\'t validated on save');
done(); done();
}).catch(function failedValidation() { }).catch(function failedValidation() {
let navItems = ctrl.get('settings.navigation'); let navItems = ctrl.settings.navigation;
expect(navItems[0].get('errors').toArray()).to.be.empty; expect(navItems[0].get('errors').toArray()).to.be.empty;
expect(navItems[1].get('errors.firstObject.attribute')).to.equal('label'); expect(navItems[1].get('errors.firstObject.attribute')).to.equal('label');
expect(navItems[2].get('errors.firstObject.attribute')).to.equal('url'); expect(navItems[2].get('errors.firstObject.attribute')).to.equal('url');
@ -74,13 +74,13 @@ describe.skip('Unit: Controller: settings/design', function () {
NavItem.create({label: '', url: ''}) NavItem.create({label: '', url: ''})
]})); ]}));
expect(ctrl.get('settings.navigation.length')).to.equal(2); expect(ctrl.settings.navigation.length).to.equal(2);
ctrl.get('save').perform().then(function passedValidation() { ctrl.get('save').perform().then(function passedValidation() {
assert(false, 'navigationItems weren\'t validated on save'); assert(false, 'navigationItems weren\'t validated on save');
done(); done();
}).catch(function failedValidation() { }).catch(function failedValidation() {
let navItems = ctrl.get('settings.navigation'); let navItems = ctrl.settings.navigation;
expect(navItems[0].get('errors').toArray()).to.be.empty; expect(navItems[0].get('errors').toArray()).to.be.empty;
done(); done();
}); });
@ -96,7 +96,7 @@ describe.skip('Unit: Controller: settings/design', function () {
]})); ]}));
}); });
expect(ctrl.get('settings.navigation.length')).to.equal(1); expect(ctrl.settings.navigation.length).to.equal(1);
ctrl.set('newNavItem.label', 'New'); ctrl.set('newNavItem.label', 'New');
ctrl.set('newNavItem.url', '/new'); ctrl.set('newNavItem.url', '/new');
@ -105,10 +105,10 @@ describe.skip('Unit: Controller: settings/design', function () {
ctrl.send('addNavItem', ctrl.get('newNavItem')); ctrl.send('addNavItem', ctrl.get('newNavItem'));
}); });
expect(ctrl.get('settings.navigation.length')).to.equal(2); expect(ctrl.settings.navigation.length).to.equal(2);
expect(ctrl.get('settings.navigation.lastObject.label')).to.equal('New'); expect(ctrl.settings.navigation.lastObject.label).to.equal('New');
expect(ctrl.get('settings.navigation.lastObject.url')).to.equal('/new'); expect(ctrl.settings.navigation.lastObject.url).to.equal('/new');
expect(ctrl.get('settings.navigation.lastObject.isNew')).to.be.false; expect(ctrl.settings.navigation.lastObject.isNew).to.be.false;
expect(ctrl.get('newNavItem.label')).to.be.empty; expect(ctrl.get('newNavItem.label')).to.be.empty;
expect(ctrl.get('newNavItem.url')).to.be.empty; expect(ctrl.get('newNavItem.url')).to.be.empty;
expect(ctrl.get('newNavItem.isNew')).to.be.true; expect(ctrl.get('newNavItem.isNew')).to.be.true;
@ -121,9 +121,9 @@ describe.skip('Unit: Controller: settings/design', function () {
ctrl.set('settings', EmberObject.create({navigation: [ ctrl.set('settings', EmberObject.create({navigation: [
NavItem.create({label: '', url: '', last: true}) NavItem.create({label: '', url: '', last: true})
]})); ]}));
expect(ctrl.get('settings.navigation.length')).to.equal(1); expect(ctrl.settings.navigation.length).to.equal(1);
ctrl.send('addNavItem', ctrl.get('settings.navigation.lastObject')); ctrl.send('addNavItem', ctrl.settings.navigation.lastObject);
expect(ctrl.get('settings.navigation.length')).to.equal(1); expect(ctrl.settings.navigation.length).to.equal(1);
}); });
}); });
@ -136,9 +136,9 @@ describe.skip('Unit: Controller: settings/design', function () {
run(() => { run(() => {
ctrl.set('settings', EmberObject.create({navigation: navItems})); ctrl.set('settings', EmberObject.create({navigation: navItems}));
expect(ctrl.get('settings.navigation').mapBy('label')).to.deep.equal(['First', 'Second']); expect(ctrl.settings.navigation.mapBy('label')).to.deep.equal(['First', 'Second']);
ctrl.send('deleteNavItem', ctrl.get('settings.navigation.firstObject')); ctrl.send('deleteNavItem', ctrl.settings.navigation.firstObject);
expect(ctrl.get('settings.navigation').mapBy('label')).to.deep.equal(['Second']); expect(ctrl.settings.navigation.mapBy('label')).to.deep.equal(['Second']);
}); });
}); });
@ -151,9 +151,9 @@ describe.skip('Unit: Controller: settings/design', function () {
run(() => { run(() => {
ctrl.set('settings', EmberObject.create({navigation: navItems})); ctrl.set('settings', EmberObject.create({navigation: navItems}));
expect(ctrl.get('settings.navigation').mapBy('url')).to.deep.equal(['/first', '/second']); expect(ctrl.settings.navigation.mapBy('url')).to.deep.equal(['/first', '/second']);
ctrl.send('updateUrl', '/new', ctrl.get('settings.navigation.firstObject')); ctrl.send('updateUrl', '/new', ctrl.settings.navigation.firstObject);
expect(ctrl.get('settings.navigation').mapBy('url')).to.deep.equal(['/new', '/second']); expect(ctrl.settings.navigation.mapBy('url')).to.deep.equal(['/new', '/second']);
}); });
}); });
}); });