diff --git a/ghost/admin/app/components/gh-post-settings-menu.hbs b/ghost/admin/app/components/gh-post-settings-menu.hbs index af6a24ea7b..897b79c798 100644 --- a/ghost/admin/app/components/gh-post-settings-menu.hbs +++ b/ghost/admin/app/components/gh-post-settings-menu.hbs @@ -79,10 +79,43 @@ {{#if this.showVisibilityInput}} -
- - -
+ {{#if (feature "multipleProducts")}} + + +
+
+
+
Public
+
+
+
+
+
+
Members-only
+
+ +
+
+
+ +
+ {{else}} +
+ + +
+ {{/if}} {{/if}} @@ -438,4 +471,4 @@ - \ No newline at end of file + diff --git a/ghost/admin/app/components/gh-post-settings-menu.js b/ghost/admin/app/components/gh-post-settings-menu.js index d6259f90f7..d1eb719a9c 100644 --- a/ghost/admin/app/components/gh-post-settings-menu.js +++ b/ghost/admin/app/components/gh-post-settings-menu.js @@ -153,6 +153,23 @@ export default Component.extend({ } }, + async setVisibility(segment) { + this.post.set('visibility', segment); + try { + await this.post.validate({property: 'visibility'}); + if (this.post.changedAttributes().visibility) { + await this.savePostTask.perform(); + } + } catch (e) { + if (!e) { + // validation error + return; + } + + throw e; + } + }, + setPublishedAtBlogTime(time) { let post = this.post; diff --git a/ghost/admin/app/models/post.js b/ghost/admin/app/models/post.js index ecd3bac53c..342d0a5f3d 100644 --- a/ghost/admin/app/models/post.js +++ b/ghost/admin/app/models/post.js @@ -171,6 +171,24 @@ export default Model.extend(Comparable, ValidationEngine, { return this.get('ghostPaths.url').join(blogUrl, previewKeyword, uuid); }), + isPublic: computed('visibility', function () { + return this.visibility === 'public' ? true : false; + }), + + visibilitySegment: computed('visibility', 'isPublic', function () { + if (this.isPublic) { + return this.settings.get('defaultContentVisibility') === 'paid' ? 'status:-free' : 'status:free,status:-free'; + } else { + if (this.visibility === 'members') { + return 'status:free,status:-free'; + } + if (this.visibility === 'paid') { + return 'status:-free'; + } + return this.visibility; + } + }), + // check every second to see if we're past the scheduled time // will only re-compute if this property is being observed elsewhere pastScheduledTime: computed('isScheduled', 'publishedAtUTC', 'clock.second', function () { diff --git a/ghost/admin/app/transforms/visibility-string.js b/ghost/admin/app/transforms/visibility-string.js new file mode 100644 index 0000000000..f1f75bab65 --- /dev/null +++ b/ghost/admin/app/transforms/visibility-string.js @@ -0,0 +1,29 @@ +import Transform from '@ember-data/serializer/transform'; + +// post visibility supports `'members'` and `'paid'` as special-case options +// but that doesn't map well for options in our token select inputs so we +// expand/convert them here to make usage elsewhere easier + +export default class VisibilityString extends Transform { + deserialize(serialized) { + if (serialized === 'members') { + return 'status:free,status:-free'; + } + if (serialized === 'paid') { + return 'status:-free'; + } + + return serialized; + } + + serialize(deserialized) { + if (deserialized === 'status:free,status:-free') { + return 'members'; + } + if (deserialized === 'status:-free') { + return 'paid'; + } + + return deserialized; + } +} diff --git a/ghost/admin/app/validators/post.js b/ghost/admin/app/validators/post.js index 5845198cd3..45f5718e40 100644 --- a/ghost/admin/app/validators/post.js +++ b/ghost/admin/app/validators/post.js @@ -67,6 +67,13 @@ export default BaseValidator.create({ } }, + visibility(model) { + if (isBlank(model.visibility) && !model.isNew) { + model.errors.add('visibility', 'A members group must be selected for members-only posts'); + this.invalidate(); + } + }, + codeinjectionFoot(model) { if (!validator.isLength(model.codeinjectionFoot || '', 0, 65535)) { model.errors.add('codeinjectionFoot', 'Footer code cannot be longer than 65535 characters.');