internal tags feature

refs TryGhost/Ghost#6165
- change behavior to use 'visibility' property
- add tests
This commit is contained in:
Austin Burdine 2016-06-13 08:21:41 -06:00
parent 05243a2dbc
commit 5d008780fd
12 changed files with 90 additions and 9 deletions

View File

@ -25,6 +25,7 @@ export default Component.extend({
isViewingSubview: false,
feature: service(),
config: service(),
mediaQueries: service(),
@ -114,11 +115,11 @@ export default Component.extend({
},
setCoverImage(image) {
invokeAction(this, 'setProperty', 'image', image);
this.send('setProperty', 'image', image);
},
clearCoverImage() {
invokeAction(this, 'setProperty', 'image', '');
this.send('setProperty', 'image', '');
},
openMeta() {

View File

@ -1,9 +1,14 @@
import Ember from 'ember';
import {invokeAction} from 'ember-invoke-action';
const {Component} = Ember;
const {
inject: {service},
Component
} = Ember;
export default Component.extend({
feature: service(),
willDestroyElement() {
this._super(...arguments);

View File

@ -1,6 +1,11 @@
import Ember from 'ember';
const {Controller, compare, computed} = Ember;
const {
Controller,
compare,
computed,
inject: {service}
} = Ember;
const {equal} = computed;
// a custom sort function is needed in order to sort the posts list the same way the server would:
@ -67,6 +72,7 @@ function publishedAtCompare(item1, item2) {
}
export default Controller.extend({
feature: service(),
showDeletePostModal: false,

View File

@ -5,7 +5,7 @@ export default Mirage.Factory.extend({
created_at() { return '2015-09-11T09:44:29.871Z'; },
created_by() { return 1; },
description(i) { return `Description for tag ${i}.`; },
hidden() { return false; },
visibility() { return 'public'; },
image(i) { return `/content/images/2015/10/tag-${i}.jpg`; },
meta_description(i) { return `Meta description for tag ${i}.`; },
meta_title(i) { return `Meta Title for tag ${i}`; },

View File

@ -125,7 +125,7 @@ export default [
id: 12,
uuid: 'd806f358-7996-4c74-b153-8876959c4b70',
key: 'labs',
value: '{"codeInjectionUI":true,"subscribers":true}',
value: '{"subscribers":true,"internalTags":true}',
type: 'blog',
created_at: '2015-01-12T18:29:01.000Z',
created_by: 1,

View File

@ -9,7 +9,7 @@ const {
computed,
inject: {service}
} = Ember;
const {equal} = computed;
const {equal, filterBy} = computed;
export default Model.extend(ValidationEngine, {
validationType: 'post',
@ -68,6 +68,7 @@ export default Model.extend(ValidationEngine, {
isPublished: equal('status', 'published'),
isDraft: equal('status', 'draft'),
internalTags: filterBy('tags', 'isInternal', true),
// remove client-generated tags, which have `id: null`.
// Ember Data won't recognize/update them automatically

View File

@ -1,8 +1,17 @@
/* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */
import Ember from 'ember';
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
import ValidationEngine from 'ghost-admin/mixins/validation-engine';
const {
computed,
observer,
inject: {service}
} = Ember;
const {equal} = computed;
export default Model.extend(ValidationEngine, {
validationType: 'tag',
@ -14,10 +23,34 @@ export default Model.extend(ValidationEngine, {
metaTitle: attr('string'),
metaDescription: attr('string'),
image: attr('string'),
hidden: attr('boolean'),
visibility: attr('string', {defaultValue: 'public'}),
createdAt: attr('moment-utc'),
updatedAt: attr('moment-utc'),
createdBy: attr(),
updatedBy: attr(),
count: attr('raw')
count: attr('raw'),
isInternal: equal('visibility', 'internal'),
isPublic: equal('visibility', 'public'),
feature: service(),
setVisibility() {
let internalRegex = /^#.?/;
this.set('visibility', internalRegex.test(this.get('name')) ? 'internal' : 'public');
},
save() {
if (this.get('feature.internalTags') && this.get('changedAttributes.name') && !this.get('isDeleted')) {
this.setVisibility();
}
return this._super(...arguments);
},
setVisibilityOnNew: observer('feature.internalTags', 'isNew', 'isSaving', 'name', function () {
if (this.get('isNew') && !this.get('isSaving') && this.get('feature.internalTags')) {
this.setVisibility();
}
})
});

View File

@ -31,6 +31,7 @@ export default Service.extend({
publicAPI: feature('publicAPI'),
subscribers: feature('subscribers'),
internalTags: feature('internalTags'),
_settings: null,

View File

@ -2,6 +2,13 @@
{{#link-to 'settings.tags.tag' tag class="tag-edit-button"}}
<span class="tag-title">{{tag.name}}</span>
<span class="label label-default">/{{tag.slug}}</span>
{{#if feature.internalTags}}
{{#if tag.isInternal}}
<span class="label label-blue">internal</span>
{{/if}}
{{/if}}
<p class="tag-description">{{tag.description}}</p>
<span class="tags-count">{{tag.count.posts}}</span>
{{/link-to}}

View File

@ -32,6 +32,11 @@
<span class="draft">Draft</span>
{{/if}}
</span>
{{#if feature.internalTags}}
{{#each post.internalTags as |tag|}}
<span class="label label-default">{{tag.name}}</span>
{{/each}}
{{/if}}
</section>
{{/link-to}}
{{/gh-posts-list-item}}

View File

@ -53,6 +53,9 @@
{{#gh-feature-flag "subscribers"}}
Subscribers - Collect email addresses from your readers, more info in <a href="http://support.ghost.org/subscribers-beta/">the docs</a>
{{/gh-feature-flag}}
{{#gh-feature-flag "internalTags"}}
Internal Tags - tags which don't show up in your theme. more info in <a href="http://support.ghost.org/internal-tags-beta/">the docs</a>.
{{/gh-feature-flag}}
</div>
</fieldset>
</form>

View File

@ -285,6 +285,25 @@ describe('Acceptance: Settings - Tags', function () {
});
});
it('shows the internal tag label', function () {
server.create('tag', {name: '#internal-tag', slug: 'hash-internal-tag', visibility: 'internal'});
visit('settings/tags/');
andThen(() => {
expect(currentURL()).to.equal('/settings/tags/hash-internal-tag');
expect(find('.settings-tags .settings-tag').length, 'tag list count')
.to.equal(1);
expect(find('.settings-tags .settings-tag:first .label.label-blue').length, 'internal tag label')
.to.equal(1);
expect(find('.settings-tags .settings-tag:first .label.label-blue').text().trim(), 'internal tag label text')
.to.equal('internal');
});
});
it('redirects to 404 when tag does not exist', function () {
server.get('/tags/slug/unknown/', function () {
return new Mirage.Response(404, {'Content-Type': 'application/json'}, {errors: [{message: 'Tag not found.', errorType: 'NotFoundError'}]});