Renamed subtitle to excerpt (#20334)

no issue

We've settled on using "excerpt" naming in place of "subtitle" to better reflect the underlying property name and tie in with themes and historical usage.

- added migration to rename the `show_subtitle` newsletter setting to `show_excerpt`
- renamed all places in the codebase that referenced subtitle
This commit is contained in:
Kevin Ansfield 2024-06-05 17:59:30 +01:00 committed by GitHub
parent 0470a4a431
commit 9ca1f3ce24
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 241 additions and 238 deletions

View File

@ -21,7 +21,7 @@ export type Newsletter = {
show_header_title: boolean;
title_font_category: string;
title_alignment: string;
show_subtitle: boolean;
show_excerpt: boolean;
show_feature_image: boolean;
body_font_category: string;
footer_content: string | null;

View File

@ -19,7 +19,7 @@
"show_header_title": true,
"title_font_category": "serif",
"title_alignment": "center",
"show_subtitle": true,
"show_excerpt": true,
"show_feature_image": true,
"body_font_category": "serif",
"footer_content": "",

View File

@ -68,13 +68,13 @@ const features = [{
description: '(Highly) Experimental support for ActivityPub.',
flag: 'ActivityPub'
},{
title: 'Subtitle in editor',
description: 'Using custom excerpt as subtitle in editor',
flag: 'editorSubtitle'
title: 'Show post excerpt inline',
description: 'Adds the excerpt input below the post title in the editor',
flag: 'editorExcerpt'
},{
title: 'Subtitle in newsletter',
description: 'Showing subtitle in newsletter',
flag: 'newsletterSubtitle'
title: 'Excerpt in newsletter',
description: 'Showing excerpt in newsletter',
flag: 'newsletterExcerpt'
}];
const AlphaFeatures: React.FC = () => {

View File

@ -103,8 +103,8 @@ const Sidebar: React.FC<{
const {mutateAsync: uploadImage} = useUploadImage();
const [selectedTab, setSelectedTab] = useState('generalSettings');
const hasEmailCustomization = useFeatureFlag('emailCustomization');
const hasNewsletterSubtitle = useFeatureFlag('newsletterSubtitle');
const hasEditorSubtitle = useFeatureFlag('editorSubtitle');
const hasNewsletterExcerpt = useFeatureFlag('newsletterExcerpt');
const hasEditorSubtitle = useFeatureFlag('editorExcerpt');
const {localSettings} = useSettingGroup();
const [siteTitle] = getSettingValues(localSettings, ['title']) as string[];
const handleError = useHandleError();
@ -419,12 +419,12 @@ const Sidebar: React.FC<{
onChange={color => updateNewsletter({title_color: color})}
/>}
<ToggleGroup gap='lg'>
{(hasNewsletterSubtitle && newsletter.show_post_title_section) &&
{(hasNewsletterExcerpt && newsletter.show_post_title_section) &&
<Toggle
checked={newsletter.show_subtitle}
checked={newsletter.show_excerpt}
direction="rtl"
label={hasEditorSubtitle ? 'Subtitle' : 'Post excerpt'}
onChange={e => updateNewsletter({show_subtitle: e.target.checked})}
onChange={e => updateNewsletter({show_excerpt: e.target.checked})}
/>
}
<Toggle

View File

@ -106,12 +106,12 @@ const NewsletterPreview: React.FC<{newsletter: Newsletter}> = ({newsletter}) =>
senderReplyTo={renderReplyToEmail(newsletter, config, supportEmailAddress, defaultEmailAddress)}
showBadge={newsletter.show_badge}
showCommentCta={showCommentCta}
showExcerpt={newsletter.show_excerpt}
showFeatureImage={newsletter.show_feature_image}
showFeedback={showFeedback}
showLatestPosts={newsletter.show_latest_posts}
showPostTitleSection={newsletter.show_post_title_section}
showSubscriptionDetails={newsletter.show_subscription_details}
showSubtitle={newsletter.show_subtitle}
siteTitle={title}
titleAlignment={newsletter.title_alignment}
titleFontCategory={newsletter.title_font_category}

View File

@ -18,7 +18,7 @@ const NewsletterPreviewContent: React.FC<{
headerTitle?: string | null;
headerSubtitle?: string | null;
showPostTitleSection: boolean;
showSubtitle: boolean;
showExcerpt: boolean;
titleAlignment?: string;
titleFontCategory?: string;
bodyFontCategory?: string;
@ -50,7 +50,7 @@ const NewsletterPreviewContent: React.FC<{
headerTitle,
headerSubtitle,
showPostTitleSection,
showSubtitle,
showExcerpt,
titleAlignment,
titleFontCategory,
bodyFontCategory,
@ -77,7 +77,7 @@ const NewsletterPreviewContent: React.FC<{
const showHeader = headerIcon || headerTitle;
const {config} = useGlobalData();
const hasNewEmailAddresses = useFeatureFlag('newEmailAddresses');
const hasNewsletterSubtitle = useFeatureFlag('newsletterSubtitle');
const hasNewsletterExcerpt = useFeatureFlag('newsletterExcerpt');
const currentDate = new Date().toLocaleDateString('default', {
year: 'numeric',
@ -101,16 +101,16 @@ const NewsletterPreviewContent: React.FC<{
<p className="leading-normal"><span className="font-semibold text-grey-900">To:</span> Jamie Larson jamie@example.com</p></>;
}
let subtitleClasses = 'mb-5 text-pretty leading-[1.7] text-black';
let excerptClasses = 'mb-5 text-pretty leading-[1.7] text-black';
if (titleFontCategory === 'serif' && bodyFontCategory === 'serif') {
subtitleClasses = clsx(subtitleClasses, 'font-serif text-[1.8rem]');
excerptClasses = clsx(excerptClasses, 'font-serif text-[1.8rem]');
} else if (titleFontCategory !== 'serif' && bodyFontCategory === 'serif') {
subtitleClasses = clsx(subtitleClasses, 'text-[1.7rem] tracking-tight');
excerptClasses = clsx(excerptClasses, 'text-[1.7rem] tracking-tight');
} else if (titleFontCategory === 'serif' && bodyFontCategory !== 'serif') {
subtitleClasses = clsx(subtitleClasses, 'font-serif text-[1.8rem]');
excerptClasses = clsx(excerptClasses, 'font-serif text-[1.8rem]');
} else {
subtitleClasses = clsx(subtitleClasses, 'text-[1.7rem] tracking-tight');
excerptClasses = clsx(excerptClasses, 'text-[1.7rem] tracking-tight');
}
return (
@ -146,8 +146,8 @@ const NewsletterPreviewContent: React.FC<{
)} style={{color: titleColor}}>
Your email newsletter
</h2>
{(hasNewsletterSubtitle && showSubtitle) && (
<p className={subtitleClasses}>A subtitle to highlight key points and engage your readers</p>
{(hasNewsletterExcerpt && showExcerpt) && (
<p className={excerptClasses}>A subtitle to highlight key points and engage your readers</p>
)}
<div className={clsx(
'flex w-full justify-between text-center text-md leading-none text-grey-700',

View File

@ -57,11 +57,11 @@
data-test-editor-title-input={{true}}
/>
{{#if (feature 'editorSubtitle')}}
{{#if (feature 'editorExcerpt')}}
<div class="relative">
<GhTextarea
@class={{concat "gh-editor-subtitle " (if @excerptErrorMessage "red")}}
@placeholder="Add a subtitle"
@class={{concat "gh-editor-excerpt " (if @excerptErrorMessage "red")}}
@placeholder="Add an excerpt"
@shouldFocus={{false}}
@tabindex="1"
@autoExpand=".gh-koenig-editor"
@ -69,12 +69,12 @@
@input={{this.onExcerptInput}}
@keyDown={{this.onExcerptKeydown}}
@didCreateTextarea={{this.registerSubtitleElement}}
data-test-textarea="subtitle"
data-test-textarea="excerpt"
/>
{{#if @excerptHasTk}}
<div
class="tk-indicator tk-indicator-subtitle"
data-testid="tk-indicator-subtitle"
class="tk-indicator tk-indicator-excerpt"
data-testid="tk-indicator-excerpt"
{{on "click" this.focusSubtitle}}
>
TK
@ -82,7 +82,7 @@
{{/if}}
<hr class="gh-editor-title-divider {{if @excerptErrorMessage "gh-editor-title-divider-error" ""}}">
{{#if @excerptErrorMessage}}
<div class="gh-editor-subtitle-error" data-test-error="subtitle">
<div class="gh-editor-excerpt-error" data-test-error="excerpt">
{{@excerptErrorMessage}}
</div>
{{/if}}
@ -96,7 +96,7 @@
@cardConfig={{@cardOptions}}
@onChange={{@onBodyChange}}
@registerAPI={{this.registerEditorAPI}}
@cursorDidExitAtTop={{if this.feature.editorSubtitle this.focusSubtitle this.focusTitle}}
@cursorDidExitAtTop={{if this.feature.editorExcerpt this.focusSubtitle this.focusTitle}}
@updateWordCount={{@updateWordCount}}
@updatePostTkCount={{@updatePostTkCount}}
/>

View File

@ -10,7 +10,7 @@ export default class GhKoenigEditorReactComponent extends Component {
containerElement = null;
titleElement = null;
subtitleElement = null;
excerptElement = null;
mousedownY = 0;
uploadUrl = `${ghostPaths().apiRoot}/images/upload/`;
@ -114,8 +114,8 @@ export default class GhKoenigEditorReactComponent extends Component {
@action
onTitleKeydown(event) {
if (this.feature.get('editorSubtitle')) {
// move cursor to the subtitle on
if (this.feature.get('editorExcerpt')) {
// move cursor to the excerpt on
// - Tab (handled by browser)
// - Arrow Down/Right when input is empty or caret at end of input
// - Enter
@ -124,7 +124,7 @@ export default class GhKoenigEditorReactComponent extends Component {
if (key === 'Enter') {
event.preventDefault();
this.subtitleElement?.focus();
this.excerptElement?.focus();
}
if ((key === 'ArrowDown' || key === 'ArrowRight') && !event.shiftKey) {
@ -132,7 +132,7 @@ export default class GhKoenigEditorReactComponent extends Component {
if (couldLeaveTitle) {
event.preventDefault();
this.subtitleElement?.focus();
this.excerptElement?.focus();
}
}
} else {
@ -167,17 +167,17 @@ export default class GhKoenigEditorReactComponent extends Component {
// Subtitle ("excerpt") Actions -------------------------------------------
@action
registerSubtitleElement(element) {
this.subtitleElement = element;
excerptSubtitleElement(element) {
this.excerptElement = element;
}
@action
focusSubtitle() {
this.subtitleElement?.focus();
this.excerptElement?.focus();
// timeout ensures this occurs after the keyboard events
setTimeout(() => {
this.subtitleElement?.setSelectionRange(-1, -1);
this.excerptElement?.setSelectionRange(-1, -1);
}, 0);
}

View File

@ -93,7 +93,7 @@
{{/if}}
{{/if}}
{{#unless (feature 'editorSubtitle')}}
{{#unless (feature 'editorExcerpt')}}
<GhFormGroup @errors={{this.post.errors}} @hasValidated={{this.post.hasValidated}} @property="customExcerpt">
<label for="custom-excerpt">Excerpt</label>
<GhTextarea

View File

@ -23,8 +23,8 @@
<div class="gh-editor-title" data-test-post-history-preview-title>
{{this.currentTitle}}
</div>
{{#if (feature "editorSubtitle")}}
<div class="gh-editor-subtitle" data-test-post-history-preview-subtitle>
{{#if (feature "editorExcerpt")}}
<div class="gh-editor-excerpt" data-test-post-history-preview-excerpt>
{{this.selectedRevision.custom_excerpt}}
</div>
<hr class="gh-editor-title-divider">

View File

@ -39,7 +39,7 @@ export default class RestoreRevisionModal extends Component {
post.featureImageAlt = revision.feature_image_alt;
post.featureImageCaption = revision.feature_image_caption;
if (this.feature.editorSubtitle) {
if (this.feature.editorExcerpt) {
post.customExcerpt = revision.custom_excerpt;
}

View File

@ -272,7 +272,7 @@ export default class LexicalEditorController extends Controller {
@computed('post.customExcerpt')
get excerptHasTk() {
if (!this.feature.editorSubtitle) {
if (!this.feature.editorExcerpt) {
return false;
}
@ -282,7 +282,7 @@ export default class LexicalEditorController extends Controller {
@computed('titleHasTk', 'excerptHasTk', 'postTkCount', 'featureImageTkCount')
get tkCount() {
const titleTk = this.titleHasTk ? 1 : 0;
const excerptTk = (this.feature.editorSubtitle && this.excerptHasTk) ? 1 : 0;
const excerptTk = (this.feature.editorExcerpt && this.excerptHasTk) ? 1 : 0;
return titleTk + excerptTk + this.postTkCount + this.featureImageTkCount;
}

View File

@ -24,7 +24,7 @@ export default class Newsletter extends Model.extend(ValidationEngine) {
@attr({defaultValue: true}) showHeaderTitle;
@attr({defaultValue: true}) showHeaderName;
@attr({defaultValue: true}) showPostTitleSection;
@attr({defaultValue: false}) showSubtitle;
@attr({defaultValue: false}) showExcerpt;
@attr({defaultValue: true}) showCommentCta;
@attr({defaultValue: false}) showSubscriptionDetails;
@attr({defaultValue: false}) showLatestPosts;

View File

@ -84,8 +84,8 @@ export default class FeatureService extends Service {
@feature('ActivityPub') ActivityPub;
@feature('internalLinking') internalLinking;
@feature('internalLinkingAtLinks') internalLinkingAtLinks;
@feature('editorSubtitle') editorSubtitle;
@feature('newsletterSubtitle') newsletterSubtitle;
@feature('editorExcerpt') editorExcerpt;
@feature('newsletterExcerpt') newsletterExcerpt;
_user = null;

View File

@ -745,7 +745,7 @@ input:focus,
/* Editor */
.gh-editor-title,
.gh-editor-subtitle {
.gh-editor-excerpt {
background: var(--white);
}
@ -1435,4 +1435,4 @@ Onboarding checklist: Share publication modal */
.gh-sidebar-banner.gh-error-banner {
background: var(--lightgrey-d1);
}
}

View File

@ -788,7 +788,7 @@ body[data-user-is-dragging] .gh-editor-feature-image-dropzone {
height: 2.4rem;
}
.gh-editor-subtitle {
.gh-editor-excerpt {
display: block;
width: 100%;
max-width: unset;
@ -807,12 +807,12 @@ body[data-user-is-dragging] .gh-editor-feature-image-dropzone {
box-shadow: none;
}
.gh-editor-subtitle:focus {
.gh-editor-excerpt:focus {
box-shadow: none !important;
border: none !important;
}
.gh-editor-subtitle-error {
.gh-editor-excerpt-error {
margin-top: .8rem;
margin-bottom: 4.8rem;
color: var(--red-d1);
@ -840,7 +840,7 @@ body[data-user-is-dragging] .gh-editor-feature-image-dropzone {
cursor: pointer;
}
.gh-editor .tk-indicator-subtitle {
.gh-editor .tk-indicator-excerpt {
top: -1px;
}

View File

@ -62,8 +62,8 @@ export default BaseValidator.create({
customExcerpt(model) {
if (!validator.isLength(model.customExcerpt || '', 0, 300)) {
if (model.feature.editorSubtitle) {
model.errors.add('customExcerpt', 'Subtitle cannot be longer than 300 characters.');
if (model.feature.editorExcerpt) {
model.errors.add('customExcerpt', 'Excerpt cannot be longer than 300 characters.');
} else {
model.errors.add('customExcerpt', 'Excerpt cannot be longer than 300 characters.');
}

View File

@ -492,19 +492,19 @@ describe('Acceptance: Editor', function () {
});
it('handles in-editor excerpt update and validation', async function () {
enableLabsFlag(this.server, 'editorSubtitle');
enableLabsFlag(this.server, 'editorExcerpt');
let post = this.server.create('post', {authors: [author], customExcerpt: 'Existing excerpt'});
await visit(`/editor/post/${post.id}`);
expect(find('[data-test-textarea="subtitle"]'), 'initial textarea').to.be.visible;
expect(find('[data-test-textarea="subtitle"]'), 'initial textarea').to.have.value('Existing excerpt');
expect(find('[data-test-textarea="excerpt"]'), 'initial textarea').to.be.visible;
expect(find('[data-test-textarea="excerpt"]'), 'initial textarea').to.have.value('Existing excerpt');
await fillIn('[data-test-textarea="subtitle"]', 'New excerpt');
expect(find('[data-test-textarea="subtitle"]'), 'updated textarea').to.have.value('New excerpt');
await fillIn('[data-test-textarea="excerpt"]', 'New excerpt');
expect(find('[data-test-textarea="excerpt"]'), 'updated textarea').to.have.value('New excerpt');
await triggerEvent('[data-test-textarea="subtitle"]', 'keydown', {
await triggerEvent('[data-test-textarea="excerpt"]', 'keydown', {
key: 's',
keyCode: 83, // s
metaKey: ctrlOrCmd === 'command',
@ -513,14 +513,14 @@ describe('Acceptance: Editor', function () {
expect(post.customExcerpt, 'saved excerpt').to.equal('New excerpt');
await fillIn('[data-test-textarea="subtitle"]', Array(302).join('a'));
await fillIn('[data-test-textarea="excerpt"]', Array(302).join('a'));
expect(find('[data-test-error="subtitle"]'), 'subtitle error').to.exist;
expect(find('[data-test-error="subtitle"]')).to.have.trimmed.text('Subtitle cannot be longer than 300 characters.');
expect(find('[data-test-error="excerpt"]'), 'excerpt error').to.exist;
expect(find('[data-test-error="excerpt"]')).to.have.trimmed.text('Excerpt cannot be longer than 300 characters.');
await fillIn('[data-test-textarea="subtitle"]', Array(300).join('a'));
await fillIn('[data-test-textarea="excerpt"]', Array(300).join('a'));
expect(find('[data-test-error="subtitle"]'), 'subtitle error').to.not.exist;
expect(find('[data-test-error="excerpt"]'), 'excerpt error').to.not.exist;
});
// https://github.com/TryGhost/Ghost/issues/11786
@ -636,28 +636,28 @@ describe('Acceptance: Editor', function () {
).to.exist;
});
it('handles TKs in subtitle', async function () {
enableLabsFlag(this.server, 'editorSubtitle');
it('handles TKs in excerpt', async function () {
enableLabsFlag(this.server, 'editorExcerpt');
const post = this.server.create('post', {authors: [author]});
await visit(`/editor/post/${post.id}`);
expect(
find('[data-test-textarea="subtitle"]').value,
'initial subtitle'
find('[data-test-textarea="excerpt"]').value,
'initial excerpt'
).to.equal('');
await fillIn('[data-test-textarea="subtitle"]', 'Test TK subtitle');
await fillIn('[data-test-textarea="excerpt"]', 'Test TK excerpt');
expect(
find('[data-test-textarea="subtitle"]').value,
'subtitle after typing'
).to.equal('Test TK subtitle');
find('[data-test-textarea="excerpt"]').value,
'excerpt after typing'
).to.equal('Test TK excerpt');
// check for TK indicator
expect(
find('[data-testid="tk-indicator-subtitle"]'),
find('[data-testid="tk-indicator-excerpt"]'),
'TK indicator text'
).to.exist;

View File

@ -26,7 +26,7 @@ describe('Acceptance: Post revisions', function () {
this.server.create('post-revision', {
post,
title: post.title,
customExcerpt: 'New subtitle',
customExcerpt: 'New excerpt',
featureImage: 'https://example.com/new-image.jpg',
featureImageAlt: 'New feature alt text',
featureImageCaption: 'New feature caption',
@ -38,7 +38,7 @@ describe('Acceptance: Post revisions', function () {
this.server.create('post-revision', {
post,
title: 'Old Title',
customExcerpt: 'Old subtitle',
customExcerpt: 'Old excerpt',
featureImage: 'https://example.com/old-image.jpg',
featureImageAlt: 'Old feature alt text',
featureImageCaption: 'Old feature caption',
@ -70,8 +70,8 @@ describe('Acceptance: Post revisions', function () {
expect(find('[data-test-post-history-preview-feature-image]')).to.have.attribute('alt', 'New feature alt text');
expect(find('[data-test-post-history-preview-feature-image-caption]')).to.have.trimmed.text('New feature caption');
// subtitle is not visible (needs feature flag)
expect(find('[data-test-post-history-preview-subtitle]')).to.not.exist;
// excerpt is not visible (needs feature flag)
expect(find('[data-test-post-history-preview-excerpt]')).to.not.exist;
// previous post can be previewed
await click('[data-test-revision-item="1"] [data-test-button="preview-revision"]');
@ -90,22 +90,22 @@ describe('Acceptance: Post revisions', function () {
expect(post.attrs.featureImageAlt).to.equal('Old feature alt text');
expect(post.attrs.featureImageCaption).to.equal('Old feature caption');
// subtitle (customExcerpt) is not restored (needs feature flag)
// excerpt (customExcerpt) is not restored (needs feature flag)
expect(post.attrs.customExcerpt).to.equal('Current excerpt');
});
it('can preview and restore subtitle (with editorSubtitle feature flag)', async function () {
enableLabsFlag(this.server, 'editorSubtitle');
it('can preview and restore excerpt (with editorExcerpt feature flag)', async function () {
enableLabsFlag(this.server, 'editorExcerpt');
const post = this.server.create('post', {
title: 'Current Title',
customExcerpt: 'Current subtitle',
customExcerpt: 'Current excerpt',
status: 'draft'
});
this.server.create('post-revision', {
post,
title: post.title,
customExcerpt: 'New subtitle',
customExcerpt: 'New excerpt',
postStatus: 'draft',
author: post.authors.models[0],
createdAt: moment(post.updatedAt).subtract(1, 'hour'),
@ -114,7 +114,7 @@ describe('Acceptance: Post revisions', function () {
this.server.create('post-revision', {
post,
title: 'Old Title',
customExcerpt: 'Old subtitle',
customExcerpt: 'Old excerpt',
postStatus: 'draft',
author: post.authors.models[0],
createdAt: moment(post.updatedAt).subtract(1, 'day'),
@ -127,19 +127,19 @@ describe('Acceptance: Post revisions', function () {
await click('[data-test-psm-trigger]');
await click('[data-test-toggle="post-history"]');
// subtitle is visible
expect(find('[data-test-post-history-preview-subtitle]')).to.exist;
expect(find('[data-test-post-history-preview-subtitle]')).to.have.trimmed.text('New subtitle');
// excerpt is visible
expect(find('[data-test-post-history-preview-excerpt]')).to.exist;
expect(find('[data-test-post-history-preview-excerpt]')).to.have.trimmed.text('New excerpt');
// previous post can be previewed
await click('[data-test-revision-item="1"] [data-test-button="preview-revision"]');
expect(find('[data-test-post-history-preview-subtitle]')).to.have.trimmed.text('Old subtitle');
expect(find('[data-test-post-history-preview-excerpt]')).to.have.trimmed.text('Old excerpt');
// previous post can be restored
await click('[data-test-revision-item="1"] [data-test-button="restore-revision"]');
await click('[data-test-modal="restore-revision"] [data-test-button="restore"]');
// post has been saved with correct data
expect(post.attrs.customExcerpt).to.equal('Old subtitle');
expect(post.attrs.customExcerpt).to.equal('Old excerpt');
});
});

View File

@ -0,0 +1,3 @@
const {createRenameColumnMigration} = require('../../utils');
module.exports = createRenameColumnMigration('newsletters', 'show_subtitle', 'show_excerpt');

View File

@ -30,7 +30,7 @@ module.exports = {
header_image: {type: 'string', maxlength: 2000, nullable: true},
show_header_icon: {type: 'boolean', nullable: false, defaultTo: true},
show_header_title: {type: 'boolean', nullable: false, defaultTo: true},
show_subtitle: {type: 'boolean', nullable: false, defaultTo: false},
show_excerpt: {type: 'boolean', nullable: false, defaultTo: false},
title_font_category: {type: 'string', maxlength: 191, nullable: false, defaultTo: 'sans_serif', validations: {isIn: [['serif', 'sans_serif']]}},
title_alignment: {type: 'string', maxlength: 191, nullable: false, defaultTo: 'center', validations: {isIn: [['center', 'left']]}},
show_feature_image: {type: 'boolean', nullable: false, defaultTo: true},

View File

@ -30,7 +30,7 @@ const Newsletter = ghostBookshelf.Model.extend({
border_color: null,
title_color: null,
feedback_enabled: false,
show_subtitle: false
show_excerpt: false
};
},

View File

@ -52,8 +52,8 @@ const ALPHA_FEATURES = [
'importMemberTier',
'lexicalIndicators',
'adminXDemo',
'editorSubtitle',
'newsletterSubtitle',
'editorExcerpt',
'newsletterExcerpt',
'internalLinkingAtLinks'
];

View File

@ -5351,7 +5351,7 @@ exports[`Members API Can subscribe to a newsletter 5: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "4604",
"content-length": "4601",
"content-type": "application/json; charset=utf-8",
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,

View File

@ -35,7 +35,7 @@ const validateRouteSettings = require('../../../../../core/server/services/route
*/
describe('DB version integrity', function () {
// Only these variables should need updating
const currentSchemaHash = 'e6fff125e9a6cd6167acaf9983a21319';
const currentSchemaHash = '7ad5a5720c29fab6b8cebb19da496935';
const currentFixturesHash = 'a489d615989eab1023d4b8af0ecee7fd';
const currentSettingsHash = '5c957ceb48c4878767d7d3db484c592d';
const currentRoutesHash = '3d180d52c663d173a6be791ef411ed01';

View File

@ -1017,14 +1017,14 @@ class EmailRenderer {
}
}
let subtitleFontClass = '';
let excerptFontClass = '';
const bodyFont = newsletter.get('body_font_category');
const titleFont = newsletter.get('title_font_category');
if (titleFont === 'serif' && bodyFont === 'serif') {
subtitleFontClass = 'post-subtitle-serif-serif';
excerptFontClass = 'post-excerpt-serif-serif';
} else if (titleFont === 'serif' && bodyFont !== 'serif') {
subtitleFontClass = 'post-subtitle-serif-sans';
excerptFontClass = 'post-excerpt-serif-sans';
}
const data = {
@ -1056,7 +1056,7 @@ class EmailRenderer {
newsletter: {
name: newsletter.get('name'),
showPostTitleSection: newsletter.get('show_post_title_section'),
showSubtitle: newsletter.get('show_subtitle'),
showExcerpt: newsletter.get('show_excerpt'),
showCommentCta: newsletter.get('show_comment_cta') && this.#settingsCache.get('comments_enabled') !== 'off' && !hasEmailOnlyFlag,
showSubscriptionDetails: newsletter.get('show_subscription_details')
},
@ -1090,7 +1090,7 @@ class EmailRenderer {
classes: {
title: 'post-title' + (newsletter.get('title_font_category') === 'serif' ? ` post-title-serif` : ``) + (newsletter.get('title_alignment') === 'left' ? ` post-title-left` : ``),
titleLink: 'post-title-link' + (newsletter.get('title_alignment') === 'left' ? ` post-title-link-left` : ``),
subtitle: 'post-subtitle' + ` ` + subtitleFontClass + (newsletter.get('title_alignment') === 'left' ? ` post-subtitle-left` : ``),
excerpt: 'post-excerpt' + ` ` + excerptFontClass + (newsletter.get('title_alignment') === 'left' ? ` post-excerpt-left` : ``),
meta: 'post-meta' + (newsletter.get('title_alignment') === 'left' ? ` post-meta-left` : ` post-meta-center`),
body: newsletter.get('body_font_category') === 'sans_serif' ? `post-content-sans-serif` : `post-content`
},

View File

@ -377,12 +377,12 @@ figure blockquote p {
text-align: left;
}
.post-subtitle-wrapper {
.post-excerpt-wrapper {
width: 100%;
max-width: 600px !important;
}
.post-subtitle {
.post-excerpt {
margin: 0;
padding-bottom: 20px;
color: #15212A;
@ -391,17 +391,17 @@ figure blockquote p {
text-align: center;
}
.post-subtitle-serif-serif {
.post-excerpt-serif-serif {
font-family: Georgia, serif;
font-size: 18px;
}
.post-subtitle-serif-sans {
.post-excerpt-serif-sans {
font-size: 18px;
font-family: Georgia, serif;
}
.post-subtitle-left {
.post-excerpt-left {
text-align: left;
}
@ -1313,7 +1313,7 @@ a[data-flickr-embed] img {
font-size: 16px;
}
table.body .post-subtitle {
table.body .post-excerpt {
font-size: 16px !important;
}

View File

@ -79,11 +79,11 @@
<a href="{{post.url}}" class="{{classes.titleLink}}">{{post.title}}</a>
</td>
</tr>
{{#hasFeature 'newsletterSubtitle'}}
{{#if (and newsletter.showSubtitle post.customExcerpt)}}
{{#hasFeature 'newsletterExcerpt'}}
{{#if (and newsletter.showExcerpt post.customExcerpt)}}
<tr>
<td class="post-subtitle-wrapper" style="width: 100%">
<p class="{{classes.subtitle}}">{{post.customExcerpt}}</p>
<td class="post-excerpt-wrapper" style="width: 100%">
<p class="{{classes.excerpt}}">{{post.customExcerpt}}</p>
</td>
</tr>
{{/if}}

View File

@ -1769,18 +1769,18 @@ describe('Email renderer', function () {
assert.equal(response.html.includes('width="auto" height="auto"'), false, 'Should not replace img height and width with auto from css');
});
describe('show subtitle', function () {
describe('show excerpt', function () {
beforeEach(function () {
labsEnabled = {
newsletterSubtitle: true
newsletterExcerpt: true
};
});
it('is rendered when enabled and customExcerpt is present', async function () {
const post = createModel(Object.assign({}, basePost, {custom_excerpt: 'This is a subtitle'}));
const post = createModel(Object.assign({}, basePost, {custom_excerpt: 'This is an excerpt'}));
const newsletter = createModel({
show_post_title_section: true,
show_subtitle: true
show_excerpt: true
});
const segment = null;
const options = {};
@ -1794,14 +1794,14 @@ describe('Email renderer', function () {
await validateHtml(response.html);
assert.equal(response.html.match(/This is a subtitle/g).length, 2, 'Subtitle should appear twice (preheader and subtitle section)');
assert.equal(response.html.match(/This is an excerpt/g).length, 2, 'Excerpt should appear twice (preheader and excerpt section)');
});
it('is not rendered when disabled and customExcerpt is present', async function () {
const post = createModel(Object.assign({}, basePost, {custom_excerpt: 'This is a subtitle'}));
const post = createModel(Object.assign({}, basePost, {custom_excerpt: 'This is an excerpt'}));
const newsletter = createModel({
show_post_title_section: true,
show_subtitle: false
show_excerpt: false
});
const segment = null;
const options = {};
@ -1815,15 +1815,15 @@ describe('Email renderer', function () {
await validateHtml(response.html);
assert.equal(response.html.match(/This is a subtitle/g).length, 1, 'Subtitle should only appear once (preheader, subtitle section skipped)');
response.html.should.not.containEql('post-subtitle-wrapper');
assert.equal(response.html.match(/This is an excerpt/g).length, 1, 'Subtitle should only appear once (preheader, excerpt section skipped)');
response.html.should.not.containEql('post-excerpt-wrapper');
});
it('does not render when enabled and customExcerpt is not present', async function () {
const post = createModel(Object.assign({}, basePost, {custom_excerpt: null}));
const newsletter = createModel({
show_post_title_section: true,
show_subtitle: true
show_excerpt: true
});
const segment = null;
const options = {};
@ -1837,7 +1837,7 @@ describe('Email renderer', function () {
await validateHtml(response.html);
response.html.should.not.containEql('post-subtitle-wrapper');
response.html.should.not.containEql('post-excerpt-wrapper');
});
});
});
@ -2073,12 +2073,12 @@ describe('Email renderer', function () {
title: 'post-title post-title-serif post-title-left',
titleLink: 'post-title-link post-title-link-left',
meta: 'post-meta post-meta-left',
subtitle: 'post-subtitle post-subtitle-serif-sans post-subtitle-left',
excerpt: 'post-excerpt post-excerpt-serif-sans post-excerpt-left',
body: 'post-content-sans-serif'
});
});
it('has correct subtitle classes for serif title+body', async function () {
it('has correct excerpt classes for serif title+body', async function () {
const html = '';
const post = createModel({
posts_meta: createModel({}),
@ -2089,10 +2089,10 @@ describe('Email renderer', function () {
title_font_category: 'serif',
title_alignment: 'left',
body_font_category: 'serif',
show_subtitle: true
show_excerpt: true
});
const data = await emailRenderer.getTemplateData({post, newsletter, html, addPaywall: false});
assert.equal(data.classes.subtitle, 'post-subtitle post-subtitle-serif-serif post-subtitle-left');
assert.equal(data.classes.excerpt, 'post-excerpt post-excerpt-serif-serif post-excerpt-left');
});
it('show comment CTA is enabled if labs disabled', async function () {