mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-24 06:35:49 +03:00
Merge branch 'master' into v3
This commit is contained in:
commit
df4bdf1cf2
@ -10,7 +10,6 @@ export default Component.extend({
|
||||
mediaQueries: service(),
|
||||
|
||||
isViewingSubview: false,
|
||||
scratchDescription: '',
|
||||
|
||||
// Allowed actions
|
||||
setProperty: () => {},
|
||||
@ -18,6 +17,7 @@ export default Component.extend({
|
||||
|
||||
scratchName: boundOneWay('member.name'),
|
||||
scratchEmail: boundOneWay('member.email'),
|
||||
scratchNote: boundOneWay('member.note'),
|
||||
subscriptions: computed('member.stripe', function () {
|
||||
let subscriptions = this.member.get('stripe');
|
||||
if (subscriptions && subscriptions.length > 0) {
|
||||
|
@ -21,8 +21,9 @@ export default Component.extend({
|
||||
yearly: yearlyPlan
|
||||
};
|
||||
subscriptionSettings.stripeConfig = stripeProcessor.config;
|
||||
subscriptionSettings.requirePaymentForSetup = !!subscriptionSettings.requirePaymentForSetup;
|
||||
subscriptionSettings.allowSelfSignup = !!subscriptionSettings.allowSelfSignup;
|
||||
subscriptionSettings.fromAddress = subscriptionSettings.fromAddress || 'noreply';
|
||||
|
||||
return subscriptionSettings;
|
||||
}),
|
||||
|
||||
@ -58,8 +59,8 @@ export default Component.extend({
|
||||
return plan;
|
||||
});
|
||||
}
|
||||
if (key === 'requirePaymentForSignup') {
|
||||
subscriptionSettings.requirePaymentForSignup = !subscriptionSettings.requirePaymentForSignup;
|
||||
if (key === 'allowSelfSignup') {
|
||||
subscriptionSettings.allowSelfSignup = !subscriptionSettings.allowSelfSignup;
|
||||
}
|
||||
if (key === 'fromAddress') {
|
||||
subscriptionSettings.fromAddress = event.target.value;
|
||||
@ -74,7 +75,7 @@ export default Component.extend({
|
||||
} catch (e) {
|
||||
return {
|
||||
isPaid: false,
|
||||
requirePaymentForSignup: false,
|
||||
allowSelfSignup: true,
|
||||
fromAddress: 'noreply',
|
||||
paymentProcessors: [{
|
||||
adapter: 'stripe',
|
||||
|
@ -12,8 +12,9 @@ export default Controller.extend({
|
||||
|
||||
router: service(),
|
||||
|
||||
member: alias('model'),
|
||||
notifications: service(),
|
||||
|
||||
member: alias('model'),
|
||||
subscribedAt: computed('member.createdAt', function () {
|
||||
let memberSince = moment(this.member.createdAt).from(moment());
|
||||
let createdDate = moment(this.member.createdAt).format('MMM DD, YYYY');
|
||||
@ -21,10 +22,10 @@ export default Controller.extend({
|
||||
}),
|
||||
|
||||
actions: {
|
||||
setProperty() {
|
||||
return;
|
||||
setProperty(propKey, value) {
|
||||
this._saveMemberProperty(propKey, value);
|
||||
},
|
||||
toggleDeleteTagModal() {
|
||||
toggleDeleteMemberModal() {
|
||||
this.toggleProperty('showDeleteMemberModal');
|
||||
},
|
||||
finaliseDeletion() {
|
||||
@ -34,9 +35,63 @@ export default Controller.extend({
|
||||
this.members.decrementProperty('meta.pagination.total');
|
||||
}
|
||||
this.router.transitionTo('members');
|
||||
},
|
||||
|
||||
toggleUnsavedChangesModal(transition) {
|
||||
let leaveTransition = this.leaveScreenTransition;
|
||||
|
||||
if (!transition && this.showUnsavedChangesModal) {
|
||||
this.set('leaveScreenTransition', null);
|
||||
this.set('showUnsavedChangesModal', false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!leaveTransition || transition.targetName === leaveTransition.targetName) {
|
||||
this.set('leaveScreenTransition', transition);
|
||||
|
||||
// if a save is running, wait for it to finish then transition
|
||||
if (this.save.isRunning) {
|
||||
return this.save.last.then(() => {
|
||||
transition.retry();
|
||||
});
|
||||
}
|
||||
|
||||
// we genuinely have unsaved data, show the modal
|
||||
this.set('showUnsavedChangesModal', true);
|
||||
}
|
||||
},
|
||||
|
||||
leaveScreen() {
|
||||
let transition = this.leaveScreenTransition;
|
||||
|
||||
if (!transition) {
|
||||
this.notifications.showAlert('Sorry, there was an error in the application. Please let the Ghost team know what happened.', {type: 'error'});
|
||||
return;
|
||||
}
|
||||
|
||||
// roll back changes on model props
|
||||
this.member.rollbackAttributes();
|
||||
|
||||
return transition.retry();
|
||||
}
|
||||
},
|
||||
|
||||
save: task(function* () {
|
||||
let member = this.member;
|
||||
try {
|
||||
return yield member.save();
|
||||
} catch (error) {
|
||||
if (error) {
|
||||
this.notifications.showAPIError(error, {key: 'member.save'});
|
||||
}
|
||||
}
|
||||
}).drop(),
|
||||
|
||||
_saveMemberProperty(propKey, newValue) {
|
||||
let member = this.member;
|
||||
member.set(propKey, newValue);
|
||||
},
|
||||
|
||||
fetchMember: task(function* (memberId) {
|
||||
this.set('isLoading', true);
|
||||
yield this.store.findRecord('member', memberId, {
|
||||
|
@ -8,7 +8,6 @@ import {
|
||||
isRequestEntityTooLargeError,
|
||||
isUnsupportedMediaTypeError
|
||||
} from 'ghost-admin/services/ajax';
|
||||
import {computed} from '@ember/object';
|
||||
import {isBlank} from '@ember/utils';
|
||||
import {isArray as isEmberArray} from '@ember/array';
|
||||
import {run} from '@ember/runloop';
|
||||
@ -63,25 +62,6 @@ export default Controller.extend({
|
||||
this.yamlMimeType = YAML_MIME_TYPE;
|
||||
},
|
||||
|
||||
subscriptionSettings: computed('settings.membersSubscriptionSettings', function () {
|
||||
let subscriptionSettings = this.parseSubscriptionSettings(this.get('settings.membersSubscriptionSettings'));
|
||||
let stripeProcessor = subscriptionSettings.paymentProcessors.find((proc) => {
|
||||
return (proc.adapter === 'stripe');
|
||||
});
|
||||
let monthlyPlan = stripeProcessor.config.plans.find(plan => plan.interval === 'month');
|
||||
let yearlyPlan = stripeProcessor.config.plans.find(plan => plan.interval === 'year');
|
||||
monthlyPlan.dollarAmount = parseInt(monthlyPlan.amount) ? (monthlyPlan.amount / 100) : 0;
|
||||
yearlyPlan.dollarAmount = parseInt(yearlyPlan.amount) ? (yearlyPlan.amount / 100) : 0;
|
||||
stripeProcessor.config.plans = {
|
||||
monthly: monthlyPlan,
|
||||
yearly: yearlyPlan
|
||||
};
|
||||
subscriptionSettings.stripeConfig = stripeProcessor.config;
|
||||
subscriptionSettings.requirePaymentForSetup = !!subscriptionSettings.requirePaymentForSetup;
|
||||
subscriptionSettings.fromAddress = subscriptionSettings.fromAddress || 'noreply';
|
||||
return subscriptionSettings;
|
||||
}),
|
||||
|
||||
actions: {
|
||||
onUpload(file) {
|
||||
let formData = new FormData();
|
||||
|
@ -4,6 +4,7 @@ import attr from 'ember-data/attr';
|
||||
export default DS.Model.extend({
|
||||
name: attr('string'),
|
||||
email: attr('string'),
|
||||
note: attr('string'),
|
||||
createdAt: attr('moment-utc'),
|
||||
stripe: attr('member-subscription')
|
||||
});
|
||||
|
@ -1,6 +1,24 @@
|
||||
import AuthenticatedRoute from 'ghost-admin/routes/authenticated';
|
||||
import CurrentUserSettings from 'ghost-admin/mixins/current-user-settings';
|
||||
import {inject as service} from '@ember/service';
|
||||
|
||||
export default AuthenticatedRoute.extend(CurrentUserSettings, {
|
||||
|
||||
router: service(),
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.router.on('routeWillChange', (transition) => {
|
||||
this.showUnsavedChangesModal(transition);
|
||||
});
|
||||
},
|
||||
|
||||
beforeModel() {
|
||||
this._super(...arguments);
|
||||
return this.get('session.user')
|
||||
.then(this.transitionAuthor());
|
||||
},
|
||||
|
||||
export default AuthenticatedRoute.extend({
|
||||
model(params) {
|
||||
this._isMemberUpdated = true;
|
||||
return this.store.findRecord('member', params.member_id, {
|
||||
@ -19,10 +37,31 @@ export default AuthenticatedRoute.extend({
|
||||
this._super(...arguments);
|
||||
|
||||
// clear the properties
|
||||
let {controller} = this;
|
||||
controller.model.rollbackAttributes();
|
||||
this.set('controller.model', null);
|
||||
this._isMemberUpdated = false;
|
||||
},
|
||||
|
||||
actions: {
|
||||
save() {
|
||||
this.controller.send('save');
|
||||
}
|
||||
},
|
||||
|
||||
titleToken() {
|
||||
return this.controller.get('member.name');
|
||||
},
|
||||
|
||||
showUnsavedChangesModal(transition) {
|
||||
if (transition.from && transition.from.name.match(/^member$/) && transition.targetName) {
|
||||
let {controller} = this;
|
||||
|
||||
if (!controller.member.isDeleted && controller.member.hasDirtyAttributes) {
|
||||
transition.abort();
|
||||
controller.send('toggleUnsavedChangesModal', transition);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
15
ghost/admin/app/serializers/member.js
Normal file
15
ghost/admin/app/serializers/member.js
Normal file
@ -0,0 +1,15 @@
|
||||
/* eslint-disable camelcase */
|
||||
import ApplicationSerializer from 'ghost-admin/serializers/application';
|
||||
|
||||
export default ApplicationSerializer.extend({
|
||||
serialize(/*snapshot, options*/) {
|
||||
let json = this._super(...arguments);
|
||||
|
||||
// Properties that exist on the model but we don't want sent in the payload
|
||||
delete json.stripe;
|
||||
// Normalize properties
|
||||
json.name = json.name || '';
|
||||
json.note = json.note || '';
|
||||
return json;
|
||||
}
|
||||
});
|
@ -4,7 +4,6 @@
|
||||
{{#gh-form-group errors=member.errors hasValidated=member.hasValidated property="name"}}
|
||||
<label for="member-name">Name</label>
|
||||
{{gh-text-input
|
||||
disabled=true
|
||||
id="member-name"
|
||||
name="name"
|
||||
value=(readonly scratchName)
|
||||
@ -29,19 +28,18 @@
|
||||
</div>
|
||||
<div class="mb6 mb0-ns w-100 w-50-ns">
|
||||
{{#gh-form-group errors=member.errors hasValidated=member.hasValidated property="note"}}
|
||||
<label for="member-description">Note</label>
|
||||
<label for="member-note">Note</label>
|
||||
{{gh-textarea
|
||||
disabled=true
|
||||
id="member-description"
|
||||
name="description"
|
||||
id="member-note"
|
||||
name="note"
|
||||
class="gh-member-details-textarea"
|
||||
tabindex="3"
|
||||
value=(readonly scratchDescription)
|
||||
input=(action (mut scratchDescription) value="target.value")
|
||||
focus-out=(action 'setProperty' 'description' scratchDescription)
|
||||
value=(readonly scratchNote)
|
||||
input=(action (mut scratchNote) value="target.value")
|
||||
focus-out=(action 'setProperty' 'note' scratchNote)
|
||||
}}
|
||||
{{gh-error-message errors=member.errors property="description"}}
|
||||
<p>Maximum: <b>500</b> characters. You’ve used {{gh-count-down-characters scratchDescription 500}}</p>
|
||||
{{gh-error-message errors=member.errors property="note"}}
|
||||
<p>Maximum: <b>500</b> characters. You’ve used {{gh-count-down-characters scratchNote 500}}</p>
|
||||
{{/gh-form-group}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,106 +1,166 @@
|
||||
<div class="flex flex-column b--whitegrey bt">
|
||||
<section class="flex flex-column bb b--whitegrey pa5">
|
||||
<div class="w-50 mb4">
|
||||
<label class="fw6 f6">Stripe publishable API key</label>
|
||||
{{gh-text-input
|
||||
value=(readonly subscriptionSettings.stripeConfig.public_token)
|
||||
input=(action "setSubscriptionSettings" "public_token")
|
||||
class="mt1"
|
||||
}}
|
||||
</div>
|
||||
<div class="w-50 mb4">
|
||||
<label class="fw6 f6 mt4">Stripe secret API key</label>
|
||||
{{gh-text-input
|
||||
value=(readonly subscriptionSettings.stripeConfig.secret_token)
|
||||
input=(action "setSubscriptionSettings" "secret_token")
|
||||
class="mt1"
|
||||
}}
|
||||
<a href="https://dashboard.stripe.com/account/apikeys" target="_blank" class="mt1 fw4 f8">
|
||||
Where to find Stripe API keys
|
||||
</a>
|
||||
|
||||
<section class="bb b--whitegrey pa5">
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<h4 class="gh-setting-title">Stripe settings</h4>
|
||||
<p class="gh-setting-desc pa0 ma0">Configure Stripe API keys for signups</p>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" class="gh-btn" {{action (toggle "membersStripeOpen" this)}} data-test-toggle-membersstripe><span>{{if membersStripeOpen "Close" "Expand"}}</span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-50 flex nb5">
|
||||
<div class="w-50 mr3">
|
||||
{{#gh-form-group}}
|
||||
<label class="fw6 f6">Monthly price</label>
|
||||
<div class="mt1 relative gh-labs-price-label gh-labs-monthly-price">
|
||||
{{#liquid-if membersStripeOpen}}
|
||||
<div class="w-50 mb4 mt5">
|
||||
<label class="fw6 f8">Stripe publishable API key</label>
|
||||
{{gh-text-input
|
||||
value=(readonly subscriptionSettings.stripeConfig.plans.monthly.dollarAmount)
|
||||
type="number"
|
||||
input=(action "setSubscriptionSettings" "month")
|
||||
}}
|
||||
value=(readonly subscriptionSettings.stripeConfig.public_token)
|
||||
input=(action "setSubscriptionSettings" "public_token")
|
||||
class="mt1"
|
||||
}}
|
||||
</div>
|
||||
{{/gh-form-group}}
|
||||
<div class="w-50 mb4">
|
||||
<label class="fw6 f8 mt4">Stripe secret API key</label>
|
||||
{{gh-text-input
|
||||
value=(readonly subscriptionSettings.stripeConfig.secret_token)
|
||||
input=(action "setSubscriptionSettings" "secret_token")
|
||||
class="mt1"
|
||||
}}
|
||||
<a href="https://dashboard.stripe.com/account/apikeys" target="_blank" class="mt1 fw4 f8">
|
||||
Where to find Stripe API keys
|
||||
</a>
|
||||
</div>
|
||||
{{/liquid-if}}
|
||||
</section>
|
||||
|
||||
<section class="bb b--whitegrey pa5">
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<h4 class="gh-setting-title">Pricing</h4>
|
||||
<p class="gh-setting-desc pa0 ma0">Set monthly and yearly subscription prices</p>
|
||||
</div>
|
||||
<div class="w-50 ml2">
|
||||
{{#gh-form-group class="description-container"}}
|
||||
<label class="fw6 f6">Yearly price</label>
|
||||
<div class="mt1 relative gh-labs-price-label gh-labs-yearly-price">
|
||||
{{gh-text-input
|
||||
value=(readonly subscriptionSettings.stripeConfig.plans.yearly.dollarAmount)
|
||||
type="number"
|
||||
input=(action "setSubscriptionSettings" "year")
|
||||
}}
|
||||
<div>
|
||||
<button type="button" class="gh-btn" {{action (toggle "membersPricingOpen" this)}} data-test-toggle-memberspricing><span>{{if membersPricingOpen "Close" "Expand"}}</span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#liquid-if membersPricingOpen}}
|
||||
<div class="w-50 flex mb4 mt5">
|
||||
<div class="w-50 mr3">
|
||||
{{#gh-form-group}}
|
||||
<label class="fw6 f8">Monthly price</label>
|
||||
<div class="mt1 relative gh-labs-price-label gh-labs-monthly-price">
|
||||
{{gh-text-input
|
||||
value=(readonly subscriptionSettings.stripeConfig.plans.monthly.dollarAmount)
|
||||
type="number"
|
||||
input=(action "setSubscriptionSettings" "month")
|
||||
}}
|
||||
</div>
|
||||
{{/gh-form-group}}
|
||||
</div>
|
||||
<div class="w-50 ml2">
|
||||
{{#gh-form-group class="description-container"}}
|
||||
<label class="fw6 f8">Yearly price</label>
|
||||
<div class="mt1 relative gh-labs-price-label gh-labs-yearly-price">
|
||||
{{gh-text-input
|
||||
value=(readonly subscriptionSettings.stripeConfig.plans.yearly.dollarAmount)
|
||||
type="number"
|
||||
input=(action "setSubscriptionSettings" "year")
|
||||
}}
|
||||
</div>
|
||||
{{/gh-form-group}}
|
||||
</div>
|
||||
</div>
|
||||
{{/liquid-if}}
|
||||
</section>
|
||||
|
||||
<section class="bb b--whitegrey pa5">
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<h4 class="gh-setting-title">Allow free members signup</h4>
|
||||
<p class="gh-setting-desc pa0 ma0">Allow free members signup</p>
|
||||
</div>
|
||||
<div>
|
||||
<div class="for-switch">
|
||||
<label class="switch" for="members-allow-self-signup"
|
||||
{{action "setSubscriptionSettings" "allowSelfSignup" bubbles="false"}}>
|
||||
<input type="checkbox" checked={{subscriptionSettings.allowSelfSignup}} class="gh-input"
|
||||
onclick={{action "setSubscriptionSettings" "allowSelfSignup"}}
|
||||
data-test-checkbox="members-allow-self-signup">
|
||||
<span class="input-toggle-component mt1"></span>
|
||||
</label>
|
||||
</div>
|
||||
{{/gh-form-group}}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="flex flex-column bb b--whitegrey pa5">
|
||||
<label class="dib f6 fw6 mb4">Default post access</label>
|
||||
|
||||
<div class="gh-radio {{if (eq settings.defaultContentVisibility "public") "active"}}"
|
||||
{{action "setDefaultContentVisibility" "public" on="click"}}>
|
||||
<div class="gh-radio-button" data-test-publishmenu-unpublished-option></div>
|
||||
<div class="gh-radio-content">
|
||||
<div class="gh-radio-label">Public</div>
|
||||
<section class="bb b--whitegrey pa5">
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<h4 class="gh-setting-title">Default post access</h4>
|
||||
<p class="gh-setting-desc pa0 ma0">Configure restrictions for new posts</p>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" class="gh-btn" {{action (toggle "membersPostAccessOpen" this)}} data-test-toggle-memberspostaccess><span>{{if membersPostAccessOpen "Close" "Expand"}}</span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gh-radio {{if (eq settings.defaultContentVisibility "members") "active"}}"
|
||||
{{action "setDefaultContentVisibility" "members" on="click"}}>
|
||||
<div class="gh-radio-button" data-test-publishmenu-published-option></div>
|
||||
<div class="gh-radio-content">
|
||||
<div class="gh-radio-label">Members only</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gh-radio {{if (eq settings.defaultContentVisibility "paid") "active"}}"
|
||||
{{action "setDefaultContentVisibility" "paid" on="click"}}>
|
||||
<div class="gh-radio-button" data-test-publishmenu-published-option></div>
|
||||
<div class="gh-radio-content">
|
||||
<div class="gh-radio-label">Paid-members only</div>
|
||||
|
||||
{{#liquid-if membersPostAccessOpen}}
|
||||
<div class="flex flex-column w-50 flex mb4 mt5">
|
||||
<div class="gh-radio {{if (eq settings.defaultContentVisibility "public") "active"}}"
|
||||
{{action "setDefaultContentVisibility" "public" on="click"}}>
|
||||
<div class="gh-radio-button" data-test-publishmenu-unpublished-option></div>
|
||||
<div class="gh-radio-content">
|
||||
<div class="gh-radio-label">Public</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gh-radio {{if (eq settings.defaultContentVisibility "members") "active"}}"
|
||||
{{action "setDefaultContentVisibility" "members" on="click"}}>
|
||||
<div class="gh-radio-button" data-test-publishmenu-published-option></div>
|
||||
<div class="gh-radio-content">
|
||||
<div class="gh-radio-label">Members only</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gh-radio {{if (eq settings.defaultContentVisibility "paid") "active"}}"
|
||||
{{action "setDefaultContentVisibility" "paid" on="click"}}>
|
||||
<div class="gh-radio-button" data-test-publishmenu-published-option></div>
|
||||
<div class="gh-radio-content">
|
||||
<div class="gh-radio-label">Paid-members only</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/liquid-if}}
|
||||
</section>
|
||||
|
||||
<div class="flex flex-column bb b--whitegrey pa5">
|
||||
<div class="for-checkbox">
|
||||
<label class="checkbox flex items-center" for="members-require-payment"
|
||||
{{action "setSubscriptionSettings" "requirePaymentForSignup" bubbles="false"}}>
|
||||
<span class="f6 fw6"> Require payment for signups</span>
|
||||
<input type="checkbox" checked={{subscriptionSettings.requirePaymentForSignup}} class="gh-input"
|
||||
onclick={{action "setSubscriptionSettings" "requirePaymentForSignup"}}
|
||||
data-test-checkbox="members-require-payment">
|
||||
<span class="input-toggle-component mt1"></span>
|
||||
</label>
|
||||
<section class="bb b--whitegrey pa5">
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<h4 class="gh-setting-title">Emails</h4>
|
||||
<p class="gh-setting-desc pa0 ma0">Membership related email settings</p>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" class="gh-btn" {{action (toggle "membersEmailOpen" this)}} data-test-toggle-membersemail><span>{{if membersEmailOpen "Close" "Expand"}}</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-column pl5 pr5 pt5">
|
||||
<div class="w-50 mr3">
|
||||
|
||||
{{#liquid-if membersEmailOpen}}
|
||||
<div class="flex flex-column w-40 flex mb2 mt5">
|
||||
{{#gh-form-group}}
|
||||
<label class="dib f6 fw6 mb4">Email</label>
|
||||
<div class="flex items-center justify-center">
|
||||
<label class="fw6 f8">Sender email address</label>
|
||||
<div class="flex items-center justify-center mt1">
|
||||
{{gh-text-input
|
||||
value=(readonly subscriptionSettings.fromAddress)
|
||||
input=(action "setSubscriptionSettings" "fromAddress")
|
||||
class="w20"
|
||||
}}
|
||||
<span class="ml3"> @{{config.blogDomain}}</span>
|
||||
</div>
|
||||
<div class="f6 fw4"> "From" address for sending sign up and sign in emails</div>
|
||||
<div class="f8 fw4 midgrey mt1"> "From" address for sign up and sign in emails</div>
|
||||
{{/gh-form-group}}
|
||||
</div>
|
||||
</div>
|
||||
{{/liquid-if}}
|
||||
</section>
|
||||
</div>
|
@ -10,16 +10,19 @@
|
||||
{{member.email}}
|
||||
{{/if}}
|
||||
</h2>
|
||||
<section class="view-actions">
|
||||
{{gh-task-button task=save class="gh-btn gh-btn-blue gh-btn-icon" data-test-button="save"}}
|
||||
</section>
|
||||
</GhCanvasHeader>
|
||||
<div class="flex items-center mb10 bt b--lightgrey-d1 pt8">
|
||||
<GhMemberAvatar @member={{member}} @sizeClass={{'f-headline fw4 lh-zero'}} class="w18 h18 mr4" />
|
||||
<GhMemberAvatar @member={{member}} @sizeClass={{if member.name 'f-subheadline fw4 lh-zero' 'f-headline fw4 lh-zero'}} class="w18 h18 mr4" />
|
||||
<div>
|
||||
<h3 class="f2 fw5 ma0 pa0">
|
||||
{{if member.name member.name member.email}}
|
||||
</h3>
|
||||
<p class="f6 pa0 ma0 midgrey">
|
||||
{{#if member.name}}
|
||||
<span class="darkgrey">{{member.email}}</span> –
|
||||
<span class="darkgrey fw5">{{member.email}}</span> –
|
||||
{{/if}}
|
||||
Member since {{this.subscribedAt}}
|
||||
</p>
|
||||
@ -28,7 +31,7 @@
|
||||
{{gh-member-settings-form member=member
|
||||
setProperty=(action "setProperty")
|
||||
isLoading=this.isLoading
|
||||
showDeleteTagModal=(action "toggleDeleteTagModal")}}
|
||||
showDeleteTagModal=(action "toggleDeleteMemberModal")}}
|
||||
</form>
|
||||
<button
|
||||
type="button"
|
||||
@ -40,6 +43,13 @@
|
||||
</button>
|
||||
</section>
|
||||
|
||||
{{#if showUnsavedChangesModal}}
|
||||
{{gh-fullscreen-modal "leave-settings"
|
||||
confirm=(action "leaveScreen")
|
||||
close=(action "toggleUnsavedChangesModal")
|
||||
modifier="action wide"}}
|
||||
{{/if}}
|
||||
|
||||
{{#if showDeleteMemberModal}}
|
||||
{{gh-fullscreen-modal "delete-member"
|
||||
model=(hash member=member onSuccess=(action "finaliseDeletion"))
|
||||
|
@ -86,43 +86,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{#if config.enableDeveloperExperiments}}
|
||||
<div class="gh-setting-header">Members (BETA) </div>
|
||||
<div class="flex flex-column br3 shadow-1 bg-grouped-table mt2">
|
||||
<div class="gh-setting-first gh-setting-last">
|
||||
<div class="gh-members-setting-content">
|
||||
<div class="flex">
|
||||
<div class="flex flex-column flex-grow-1">
|
||||
<div class="gh-setting-title pl5 pt5">Members</div>
|
||||
<div class="gh-setting-desc pl5 pb5">Enable free or paid member registration.</div>
|
||||
</div>
|
||||
<div class="gh-setting-action">
|
||||
<div class="for-switch pa5">{{gh-feature-flag "members"}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#liquid-if feature.labs.members}}
|
||||
{{gh-members-lab-setting
|
||||
settings=settings
|
||||
setDefaultContentVisibility=(action "setDefaultContentVisibility")
|
||||
setMembersSubscriptionSettings=(action "setMembersSubscriptionSettings")
|
||||
}}
|
||||
|
||||
<div class="mb2 mt5 pl5 pr5 pb5">
|
||||
{{gh-task-button "Save members settings"
|
||||
task=saveSettings
|
||||
successText="Saved"
|
||||
runningText="Saving"
|
||||
class="gh-btn gh-btn-blue gh-btn-icon"
|
||||
}}
|
||||
</div>
|
||||
{{/liquid-if}}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
|
||||
<div class="gh-setting-header">Beta features</div>
|
||||
<div class="flex flex-column br3 shadow-1 bg-grouped-table pa5 mt2">
|
||||
<div class="gh-setting-first">
|
||||
@ -236,6 +200,43 @@
|
||||
{{/gh-uploader}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if config.enableDeveloperExperiments}}
|
||||
<div class="gh-setting-header">Members (BETA) </div>
|
||||
<div class="flex flex-column br3 shadow-1 bg-grouped-table mt2">
|
||||
<div class="gh-setting-first gh-setting-last">
|
||||
<div class="gh-members-setting-content">
|
||||
<div class="flex">
|
||||
<div class="flex flex-column flex-grow-1">
|
||||
<div class="gh-setting-title pl5 pt5">Members</div>
|
||||
<div class="gh-setting-desc pl5 pb5">Enable membership for your site</div>
|
||||
</div>
|
||||
<div class="gh-setting-action">
|
||||
<div class="for-switch pa5">{{gh-feature-flag "members"}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#liquid-if feature.labs.members}}
|
||||
{{gh-members-lab-setting
|
||||
settings=settings
|
||||
setDefaultContentVisibility=(action "setDefaultContentVisibility")
|
||||
setMembersSubscriptionSettings=(action "setMembersSubscriptionSettings")
|
||||
}}
|
||||
|
||||
<div class="mt5 pl5 pr5 pb5">
|
||||
{{gh-task-button "Save members settings"
|
||||
task=saveSettings
|
||||
successText="Saved"
|
||||
runningText="Saving"
|
||||
class="gh-btn gh-btn-blue gh-btn-icon"
|
||||
}}
|
||||
</div>
|
||||
{{/liquid-if}}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</section>
|
||||
</section>
|
||||
|
||||
|
@ -18,13 +18,7 @@ export default Transform.extend({
|
||||
|
||||
if (isEmberArray(deserialized)) {
|
||||
subscriptionArray = deserialized.map((item) => {
|
||||
let adapter = item.get('adapter').trim();
|
||||
let amount = item.get('amount');
|
||||
let plan = item.get('plan').trim();
|
||||
let status = item.get('status').trim();
|
||||
let validUntil = item.get('validUntil');
|
||||
|
||||
return {adapter, amount, plan, status, validUntil};
|
||||
return item;
|
||||
}).compact();
|
||||
} else {
|
||||
subscriptionArray = [];
|
||||
|
@ -177,7 +177,7 @@ export default [
|
||||
id: 23,
|
||||
type: 'members',
|
||||
key: 'members_subscription_settings',
|
||||
value: '{"isPaid":false,"requirePaymentForSignup":false,"fromAddress":"noreply","paymentProcessors":[{"adapter":"stripe","config":{"secret_token":"","public_token":"","product":{"name":"Ghost Subscription"},"plans":[{"name":"Monthly","currency":"usd","interval":"month","amount":""},{"name":"Yearly","currency":"usd","interval":"year","amount":""}]}}]}',
|
||||
value: '{"isPaid":false,"allowSelfSignup":true,"fromAddress":"noreply","paymentProcessors":[{"adapter":"stripe","config":{"secret_token":"","public_token":"","product":{"name":"Ghost Subscription"},"plans":[{"name":"Monthly","currency":"usd","interval":"month","amount":""},{"name":"Yearly","currency":"usd","interval":"year","amount":""}]}}]}',
|
||||
created_at: '2019-10-09T09:49:00.000Z',
|
||||
created_by: 1,
|
||||
updated_at: '2019-10-09T09:49:00.000Z',
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ghost-admin",
|
||||
"version": "2.34.0",
|
||||
"version": "2.35.0",
|
||||
"description": "Ember.js admin client for Ghost",
|
||||
"author": "Ghost Foundation",
|
||||
"homepage": "http://ghost.org",
|
||||
|
Loading…
Reference in New Issue
Block a user