️ Add "Excerpt" field to post settings menu (#810)

refs TryGhost/Ghost#8793

- add `customExcerpt` attr to Post model + reorder attrs to be alphabetical
- add "Excerpt" field to PSM
- add validation for `customExcerpt` length (max 300 chars)
- add style adjustments for custom excerpt UI
This commit is contained in:
Kevin Ansfield 2017-08-01 12:24:46 +04:00 committed by Aileen Nowak
parent 68d2164672
commit fb930b0698
7 changed files with 93 additions and 18 deletions

View File

@ -30,6 +30,7 @@ export default Component.extend(SettingsMenuMixin, {
model: null,
slugValue: boundOneWay('model.slug'),
customExcerptScratch: alias('model.customExcerptScratch'),
metaTitleScratch: alias('model.metaTitleScratch'),
metaDescriptionScratch: alias('model.metaDescriptionScratch'),
@ -245,6 +246,21 @@ export default Component.extend(SettingsMenuMixin, {
}
},
setCustomExcerpt(excerpt) {
let model = this.get('model');
let currentExcerpt = model.get('customExcerpt');
if (excerpt === currentExcerpt) {
return;
}
model.set('customExcerpt', excerpt);
return model.validate({property: 'customExcerpt'}).then(() => {
return model.save();
});
},
setMetaTitle(metaTitle) {
// Grab the model and current stored meta title
let model = this.get('model');

View File

@ -158,6 +158,7 @@ export default Mixin.create({
}
this.set('model.title', this.get('model.titleScratch'));
this.set('model.customExcerpt', this.get('model.customExcerptScratch'));
this.set('model.metaTitle', this.get('model.metaTitleScratch'));
this.set('model.metaDescription', this.get('model.metaDescriptionScratch'));

View File

@ -73,32 +73,33 @@ export default Model.extend(Comparable, ValidationEngine, {
validationType: 'post',
uuid: attr('string'),
title: attr('string', {defaultValue: ''}),
slug: attr('string'),
mobiledoc: attr('json-string', {defaultValue: () => BLANK_DOC}),
html: attr('string'),
featureImage: attr('string'),
featured: attr('boolean', {defaultValue: false}),
page: attr('boolean', {defaultValue: false}),
plaintext: attr('string'),
status: attr('string', {defaultValue: 'draft'}),
locale: attr('string'),
metaTitle: attr('string'),
metaDescription: attr('string'),
author: belongsTo('user', {async: true}),
authorId: attr('string'),
updatedAtUTC: attr('moment-utc'),
updatedBy: attr(),
publishedAtUTC: attr('moment-utc'),
publishedBy: belongsTo('user', {async: true}),
createdAtUTC: attr('moment-utc'),
createdBy: attr(),
customExcerpt: attr(),
featured: attr('boolean', {defaultValue: false}),
featureImage: attr('string'),
html: attr('string'),
locale: attr('string'),
metaDescription: attr('string'),
metaTitle: attr('string'),
mobiledoc: attr('json-string', {defaultValue: () => BLANK_DOC}),
page: attr('boolean', {defaultValue: false}),
plaintext: attr('string'),
publishedAtUTC: attr('moment-utc'),
publishedBy: belongsTo('user', {async: true}),
slug: attr('string'),
status: attr('string', {defaultValue: 'draft'}),
tags: hasMany('tag', {
embedded: 'always',
async: false
}),
title: attr('string', {defaultValue: ''}),
updatedAtUTC: attr('moment-utc'),
updatedBy: attr(),
url: attr('string'),
uuid: attr('string'),
scratch: null,
titleScratch: null,
@ -114,6 +115,7 @@ export default Model.extend(Comparable, ValidationEngine, {
publishedAtBlogDate: '',
publishedAtBlogTime: '',
customExcerptScratch: boundOneWay('customExcerpt'),
metaTitleScratch: boundOneWay('metaTitle'),
metaDescriptionScratch: boundOneWay('metaDescription'),

View File

@ -179,6 +179,15 @@
stroke: color(var(--red) lightness(-10%));
}
.settings-menu-content .selectize-input {
padding: 7px 12px;
}
.post-setting-custom-excerpt {
font-size: 1.5rem;
line-height: 1.35em;
}
/* Background
/* ---------------------------------------------------------- */

View File

@ -73,6 +73,12 @@
plugins="remove_button, drag_drop"}}
</div>
{{#gh-form-group errors=model.errors hasValidated=model.hasValidated property="customExcerpt"}}
<label for="custom-excerpt">Excerpt</label>
{{gh-textarea customExcerptScratch class="post-setting-custom-excerpt" id="custom-excerpt" name="post-setting-custom-excerpt" focusOut=(action "setCustomExcerpt" customExcerptScratch) stopEnterKeyDownPropagation="true" update=(action (mut customExcerptScratch)) data-test-field="custom-excerpt"}}
{{gh-error-message errors=model.errors property="customExcerpt" data-test-error="custom-excerpt"}}
{{/gh-form-group}}
{{#unless session.user.isAuthor}}
<div class="form-group for-select">
<label for="author-list">Author</label>

View File

@ -3,7 +3,7 @@ import moment from 'moment';
import {isEmpty, isPresent} from 'ember-utils';
export default BaseValidator.create({
properties: ['title', 'metaTitle', 'metaDescription', 'publishedAtBlogTime', 'publishedAtBlogDate'],
properties: ['title', 'customExcerpt', 'metaTitle', 'metaDescription', 'publishedAtBlogTime', 'publishedAtBlogDate'],
title(model) {
let title = model.get('title');
@ -19,6 +19,15 @@ export default BaseValidator.create({
}
},
customExcerpt(model) {
let customExcerpt = model.get('customExcerpt');
if (!validator.isLength(customExcerpt, 0, 300)) {
model.get('errors').add('customExcerpt', 'Excerpt cannot be longer than 300 characters.');
this.invalidate();
}
},
metaTitle(model) {
let metaTitle = model.get('metaTitle');

View File

@ -548,5 +548,37 @@ describe('Acceptance: Editor', function() {
'url after autosave'
).to.equal('/editor/1');
});
it('saves post settings fields', async function () {
let post = server.create('post');
await visit(`/editor/${post.id}`);
// TODO: implement tests for other fields
// changing custom excerpt auto-saves
await click(testSelector('psm-trigger'));
await fillIn(testSelector('field', 'custom-excerpt'), 'Testing excerpt');
await triggerEvent(testSelector('field', 'custom-excerpt'), 'blur');
expect(
server.db.posts[0].custom_excerpt,
'saved excerpt'
).to.equal('Testing excerpt');
// excerpt has validation
await fillIn(testSelector('field', 'custom-excerpt'), Array(302).join('a'));
await triggerEvent(testSelector('field', 'custom-excerpt'), 'blur');
expect(
find(testSelector('error', 'custom-excerpt')).text().trim(),
'excerpt too long error'
).to.match(/cannot be longer than 300/);
expect(
server.db.posts[0].custom_excerpt,
'saved excerpt after validation error'
).to.equal('Testing excerpt');
});
});
});