mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-27 21:03:29 +03:00
⚡️ 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:
parent
68d2164672
commit
fb930b0698
@ -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');
|
||||
|
@ -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'));
|
||||
|
||||
|
@ -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'),
|
||||
|
||||
|
@ -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
|
||||
/* ---------------------------------------------------------- */
|
||||
|
@ -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>
|
||||
|
@ -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');
|
||||
|
||||
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user