Removed links and figcaptions from excerpts

refs: https://github.com/TryGhost/Team/issues/1609
refs: https://github.com/TryGhost/Ghost/issues/11532
refs: https://github.com/TryGhost/Ghost/issues/11407

- these don't read correctly in an excert context
This commit is contained in:
Hannah Wolfe 2022-05-13 16:23:30 +01:00
parent 338dc3ae6c
commit 9d4d6688d8
6 changed files with 103 additions and 65 deletions

View File

@ -1,3 +1,13 @@
const _ = require('lodash');
const mergeSettings = (extraSettings) => {
return _.mergeWith({}, baseSettings, extraSettings, function customizer(objValue, srcValue) {
if (_.isArray(objValue)) {
return objValue.concat(srcValue);
}
});
};
const baseSettings = {
wordwrap: false,
preserveNewlines: true,
@ -17,9 +27,6 @@ const baseSettings = {
{selector: 'h6', options: {uppercase: false}},
{selector: 'table', options: {uppercaseHeaderCells: false}},
// equiv hideLinkHrefIfSameAsText: true
{selector: 'a', options: {hideLinkHrefIfSameAsText: true}},
// Backwards compatibility with html-to-text 5.1.1
{selector: 'div', format: 'inline'}
]
@ -35,8 +42,22 @@ const loadConverters = () => {
const {compile} = require('html-to-text');
excerptConverter = compile(baseSettings);
emailConverter = compile(baseSettings);
const excerptSettings = mergeSettings({
selectors: [
{selector: 'a', options: {ignoreHref: true}},
{selector: 'figcaption', format: 'skip'}
]
});
const emailSettings = mergeSettings({
selectors: [
// equiv hideLinkHrefIfSameAsText: true
{selector: 'a', options: {hideLinkHrefIfSameAsText: true}}
]
});
excerptConverter = compile(excerptSettings);
emailConverter = compile(emailSettings);
};
module.exports.excerpt = (html) => {

View File

@ -122,11 +122,11 @@ An about page is a great example of one you might want to set up early on so peo
For example, here's how to reach us!
* @Ghost [https://twitter.com/ghost] on Twitter
* @Ghost [https://www.facebook.com/ghost] on Facebook
* @Ghost [https://instagram.com/ghost] on Instagram
* @Ghost on Twitter
* @Ghost on Facebook
* @Ghost on Instagram
If you prefer to use a contact form, almost all of the great embedded form services work great with Ghost and a",
If you prefer to use a contact form, almost all of the great embedded form services work great with Ghost and are easy to set up:",
"feature_image": null,
"feature_image_alt": null,
"feature_image_caption": null,
@ -165,7 +165,8 @@ If you prefer to use a contact form, almost all of the great embedded form servi
Ghost is a non-profit organization, and we give away all our intellectual property as open source software. If you believe in what we do, there are a number of ways you can give us a hand, and we hugely appreciate all of them:
* Contribute code via GitHub [https://gith",
* Contribute code via GitHub
* Contribute",
"feature_image": null,
"feature_image_alt": null,
"feature_image_caption": null,
@ -273,7 +274,7 @@ exports[`Pages Content API Can request pages 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "*",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "9196",
"content-length": "9124",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Accept-Encoding",

View File

@ -260,9 +260,9 @@ Object {
"custom_template": null,
"email_subject": null,
"excerpt": " * Lorem
* Aliquam [http://127.0.0.1:2369/about#nowhere]
* Tortor [//somewhere.com/link#nowhere]
* Morbi [http://somewhere.com/link#nowhere]
* Aliquam
* Tortor
* Morbi
* Praesent
* Pellentesque
@ -270,7 +270,7 @@ Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac tu
1234abcdefghijkl
Definition listConsectetur ad",
Definition listConsectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim venia",
"feature_image": null,
"feature_image_alt": null,
"feature_image_caption": null,
@ -619,9 +619,11 @@ Definition listConsectetur ad",
"email_subject": null,
"excerpt": "For creators and aspiring entrepreneurs looking to generate a sustainable recurring revenue stream from their creative work, Ghost has built-in payments allowing you to create a subscription commerce business.
Connect your Stripe [https://stripe.com] account to Ghost, and you'll be able to quickly and easily create monthly and yearly premium plans for members to subscribe to, as well as complimentary plans for friends and family.
Connect your Stripe account to Ghost, and you'll be able to quickly and easily create monthly and yearly premium plans for members to subscribe to, as well as complimentary plans for friends and family.
Ghost takes 0% payment fees, so everything you make is yours to ",
Ghost takes 0% payment fees, so everything you make is yours to keep!
Using subscrip",
"feature_image": "https://static.ghost.org/v4.0.0/images/organizing-your-content.png",
"feature_image_alt": null,
"feature_image_caption": null,
@ -636,16 +638,14 @@ Ghost takes 0% payment fees, so everything you make is yours to ",
"og_title": null,
"plaintext": "For creators and aspiring entrepreneurs looking to generate a sustainable recurring revenue stream from their creative work, Ghost has built-in payments allowing you to create a subscription commerce business.
Connect your Stripe [https://stripe.com] account to Ghost, and you'll be able to quickly and easily create monthly and yearly premium plans for members to subscribe to, as well as complimentary plans for friends and family.
Connect your Stripe account to Ghost, and you'll be able to quickly and easily create monthly and yearly premium plans for members to subscribe to, as well as complimentary plans for friends and family.
Ghost takes 0% payment fees, so everything you make is yours to keep!
Using subscriptions, you can build an independent media business like Stratechery [https://stratechery.com], The Information [https://www.theinformation.com], or The Browser [https://thebrowser.com].
Using subscriptions, you can build an independent media business like Stratechery, The Information, or The Browser.
The creator economy is just getting started, and Ghost allows you to build something based on technology that you own and control.
https://thebrowser.comThe Browser has over 10,000 paying subscribers
Most successful subscription businesses publish a mix of free and paid posts to attract a new audience, and upsell the most loyal members to a premium offering. You can also mix different access levels within the same post, showing a free preview to logged out members and then, right when you're ready for a cliffhanger, that's a good time to...",
"primary_author": Object {
"bio": "You can delete this user to remove all the welcome posts",
@ -1036,7 +1036,7 @@ exports[`Posts Content API Can filter posts by authors 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "*",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "54894",
"content-length": "54719",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Accept-Encoding",
@ -1068,9 +1068,9 @@ Object {
"custom_template": null,
"email_subject": null,
"excerpt": " * Lorem
* Aliquam [http://127.0.0.1:2369/about#nowhere]
* Tortor [//somewhere.com/link#nowhere]
* Morbi [http://somewhere.com/link#nowhere]
* Aliquam
* Tortor
* Morbi
* Praesent
* Pellentesque
@ -1078,7 +1078,7 @@ Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac tu
1234abcdefghijkl
Definition listConsectetur ad",
Definition listConsectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim venia",
"feature_image": null,
"feature_image_alt": null,
"feature_image_caption": null,
@ -1748,9 +1748,11 @@ Object {
"email_subject": null,
"excerpt": "For creators and aspiring entrepreneurs looking to generate a sustainable recurring revenue stream from their creative work, Ghost has built-in payments allowing you to create a subscription commerce business.
Connect your Stripe [https://stripe.com] account to Ghost, and you'll be able to quickly and easily create monthly and yearly premium plans for members to subscribe to, as well as complimentary plans for friends and family.
Connect your Stripe account to Ghost, and you'll be able to quickly and easily create monthly and yearly premium plans for members to subscribe to, as well as complimentary plans for friends and family.
Ghost takes 0% payment fees, so everything you make is yours to ",
Ghost takes 0% payment fees, so everything you make is yours to keep!
Using subscrip",
"feature_image": "https://static.ghost.org/v4.0.0/images/organizing-your-content.png",
"feature_image_alt": null,
"feature_image_caption": null,
@ -1765,16 +1767,14 @@ Ghost takes 0% payment fees, so everything you make is yours to ",
"og_title": null,
"plaintext": "For creators and aspiring entrepreneurs looking to generate a sustainable recurring revenue stream from their creative work, Ghost has built-in payments allowing you to create a subscription commerce business.
Connect your Stripe [https://stripe.com] account to Ghost, and you'll be able to quickly and easily create monthly and yearly premium plans for members to subscribe to, as well as complimentary plans for friends and family.
Connect your Stripe account to Ghost, and you'll be able to quickly and easily create monthly and yearly premium plans for members to subscribe to, as well as complimentary plans for friends and family.
Ghost takes 0% payment fees, so everything you make is yours to keep!
Using subscriptions, you can build an independent media business like Stratechery [https://stratechery.com], The Information [https://www.theinformation.com], or The Browser [https://thebrowser.com].
Using subscriptions, you can build an independent media business like Stratechery, The Information, or The Browser.
The creator economy is just getting started, and Ghost allows you to build something based on technology that you own and control.
https://thebrowser.comThe Browser has over 10,000 paying subscribers
Most successful subscription businesses publish a mix of free and paid posts to attract a new audience, and upsell the most loyal members to a premium offering. You can also mix different access levels within the same post, showing a free preview to logged out members and then, right when you're ready for a cliffhanger, that's a good time to...",
"primary_author": Object {
"bio": "You can delete this user to remove all the welcome posts",
@ -1984,9 +1984,9 @@ Most successful subscription businesses publish a mix of free and paid posts to
"custom_template": null,
"email_subject": null,
"excerpt": " * Lorem
* Aliquam [http://127.0.0.1:2369/about#nowhere]
* Tortor [//somewhere.com/link#nowhere]
* Morbi [http://somewhere.com/link#nowhere]
* Aliquam
* Tortor
* Morbi
* Praesent
* Pellentesque
@ -1994,7 +1994,7 @@ Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac tu
1234abcdefghijkl
Definition listConsectetur ad",
Definition listConsectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim venia",
"feature_image": null,
"feature_image_alt": null,
"feature_image_caption": null,
@ -2281,7 +2281,7 @@ exports[`Posts Content API Can include relations 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "*",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "65223",
"content-length": "65048",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Accept-Encoding",
@ -2582,9 +2582,11 @@ Object {
"email_subject": null,
"excerpt": "For creators and aspiring entrepreneurs looking to generate a sustainable recurring revenue stream from their creative work, Ghost has built-in payments allowing you to create a subscription commerce business.
Connect your Stripe [https://stripe.com] account to Ghost, and you'll be able to quickly and easily create monthly and yearly premium plans for members to subscribe to, as well as complimentary plans for friends and family.
Connect your Stripe account to Ghost, and you'll be able to quickly and easily create monthly and yearly premium plans for members to subscribe to, as well as complimentary plans for friends and family.
Ghost takes 0% payment fees, so everything you make is yours to ",
Ghost takes 0% payment fees, so everything you make is yours to keep!
Using subscrip",
"feature_image": "https://static.ghost.org/v4.0.0/images/organizing-your-content.png",
"feature_image_alt": null,
"feature_image_caption": null,
@ -2599,16 +2601,14 @@ Ghost takes 0% payment fees, so everything you make is yours to ",
"og_title": null,
"plaintext": "For creators and aspiring entrepreneurs looking to generate a sustainable recurring revenue stream from their creative work, Ghost has built-in payments allowing you to create a subscription commerce business.
Connect your Stripe [https://stripe.com] account to Ghost, and you'll be able to quickly and easily create monthly and yearly premium plans for members to subscribe to, as well as complimentary plans for friends and family.
Connect your Stripe account to Ghost, and you'll be able to quickly and easily create monthly and yearly premium plans for members to subscribe to, as well as complimentary plans for friends and family.
Ghost takes 0% payment fees, so everything you make is yours to keep!
Using subscriptions, you can build an independent media business like Stratechery [https://stratechery.com], The Information [https://www.theinformation.com], or The Browser [https://thebrowser.com].
Using subscriptions, you can build an independent media business like Stratechery, The Information, or The Browser.
The creator economy is just getting started, and Ghost allows you to build something based on technology that you own and control.
https://thebrowser.comThe Browser has over 10,000 paying subscribers
Most successful subscription businesses publish a mix of free and paid posts to attract a new audience, and upsell the most loyal members to a premium offering. You can also mix different access levels within the same post, showing a free preview to logged out members and then, right when you're ready for a cliffhanger, that's a good time to...",
"published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/,
"reading_time": 1,
@ -2704,9 +2704,9 @@ Most successful subscription businesses publish a mix of free and paid posts to
"custom_template": null,
"email_subject": null,
"excerpt": " * Lorem
* Aliquam [http://127.0.0.1:2369/about#nowhere]
* Tortor [//somewhere.com/link#nowhere]
* Morbi [http://somewhere.com/link#nowhere]
* Aliquam
* Tortor
* Morbi
* Praesent
* Pellentesque
@ -2714,7 +2714,7 @@ Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac tu
1234abcdefghijkl
Definition listConsectetur ad",
Definition listConsectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim venia",
"feature_image": null,
"feature_image_alt": null,
"feature_image_caption": null,
@ -2870,7 +2870,7 @@ exports[`Posts Content API Can request posts 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "*",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "46342",
"content-length": "46167",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Accept-Encoding",
@ -3055,9 +3055,11 @@ Object {
"email_subject": null,
"excerpt": "For creators and aspiring entrepreneurs looking to generate a sustainable recurring revenue stream from their creative work, Ghost has built-in payments allowing you to create a subscription commerce business.
Connect your Stripe [https://stripe.com] account to Ghost, and you'll be able to quickly and easily create monthly and yearly premium plans for members to subscribe to, as well as complimentary plans for friends and family.
Connect your Stripe account to Ghost, and you'll be able to quickly and easily create monthly and yearly premium plans for members to subscribe to, as well as complimentary plans for friends and family.
Ghost takes 0% payment fees, so everything you make is yours to ",
Ghost takes 0% payment fees, so everything you make is yours to keep!
Using subscrip",
"feature_image": "https://static.ghost.org/v4.0.0/images/organizing-your-content.png",
"feature_image_alt": null,
"feature_image_caption": null,
@ -3072,16 +3074,14 @@ Ghost takes 0% payment fees, so everything you make is yours to ",
"og_title": null,
"plaintext": "For creators and aspiring entrepreneurs looking to generate a sustainable recurring revenue stream from their creative work, Ghost has built-in payments allowing you to create a subscription commerce business.
Connect your Stripe [https://stripe.com] account to Ghost, and you'll be able to quickly and easily create monthly and yearly premium plans for members to subscribe to, as well as complimentary plans for friends and family.
Connect your Stripe account to Ghost, and you'll be able to quickly and easily create monthly and yearly premium plans for members to subscribe to, as well as complimentary plans for friends and family.
Ghost takes 0% payment fees, so everything you make is yours to keep!
Using subscriptions, you can build an independent media business like Stratechery [https://stratechery.com], The Information [https://www.theinformation.com], or The Browser [https://thebrowser.com].
Using subscriptions, you can build an independent media business like Stratechery, The Information, or The Browser.
The creator economy is just getting started, and Ghost allows you to build something based on technology that you own and control.
https://thebrowser.comThe Browser has over 10,000 paying subscribers
Most successful subscription businesses publish a mix of free and paid posts to attract a new audience, and upsell the most loyal members to a premium offering. You can also mix different access levels within the same post, showing a free preview to logged out members and then, right when you're ready for a cliffhanger, that's a good time to...",
"published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/,
"reading_time": 1,
@ -3177,9 +3177,9 @@ Most successful subscription businesses publish a mix of free and paid posts to
"custom_template": null,
"email_subject": null,
"excerpt": " * Lorem
* Aliquam [http://127.0.0.1:2369/about#nowhere]
* Tortor [//somewhere.com/link#nowhere]
* Morbi [http://somewhere.com/link#nowhere]
* Aliquam
* Tortor
* Morbi
* Praesent
* Pellentesque
@ -3187,7 +3187,7 @@ Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac tu
1234abcdefghijkl
Definition listConsectetur ad",
Definition listConsectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim venia",
"feature_image": null,
"feature_image_alt": null,
"feature_image_caption": null,
@ -3343,7 +3343,7 @@ exports[`Posts Content API Can request posts from different origin 2: [headers]
Object {
"access-control-allow-origin": "*",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "46342",
"content-length": "46167",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Accept-Encoding",

View File

@ -249,7 +249,7 @@ describe('api/canary/content/posts', function () {
localUtils.API.checkResponse(res.body.posts[0], 'post', null, null, ['id', 'title', 'slug', 'excerpt', 'plaintext']);
// excerpt should transform links to absolute URLs
res.body.posts[0].excerpt.should.match(/\* Aliquam \[http:\/\/127.0.0.1:2369\/about#nowhere\]/);
res.body.posts[0].excerpt.should.match(/\* Aliquam/);
});
});

View File

@ -1146,7 +1146,7 @@ describe('Post Model', function () {
models.Post.add(post, context).then((createdPost) => {
createdPost.get('mobiledoc').should.equal('{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"http://127.0.0.1:2369/content/images/card.jpg"}]],"markups":[["a",["href","http://127.0.0.1:2369/test"]]],"sections":[[1,"p",[[0,[0],1,"Testing"]]],[10,0]]}');
createdPost.get('html').should.equal('<p><a href="http://127.0.0.1:2369/test">Testing</a></p><figure class="kg-card kg-image-card"><img src="http://127.0.0.1:2369/content/images/card.jpg" class="kg-image" alt loading="lazy"></figure>');
createdPost.get('plaintext').should.containEql('Testing [http://127.0.0.1:2369/test]');
createdPost.get('plaintext').should.containEql('Testing');
createdPost.get('custom_excerpt').should.equal('Testing <a href="http://127.0.0.1:2369/internal">links</a> in custom excerpts');
createdPost.get('codeinjection_head').should.equal('<script src="http://127.0.0.1:2369/assets/head.js"></script>');
createdPost.get('codeinjection_foot').should.equal('<script src="http://127.0.0.1:2369/assets/foot.js"></script>');
@ -1175,7 +1175,7 @@ describe('Post Model', function () {
const [knexPost] = knexResult;
knexPost.mobiledoc.should.equal('{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"__GHOST_URL__/content/images/card.jpg"}]],"markups":[["a",["href","__GHOST_URL__/test"]]],"sections":[[1,"p",[[0,[0],1,"Testing"]]],[10,0]]}');
knexPost.html.should.equal('<p><a href="__GHOST_URL__/test">Testing</a></p><figure class="kg-card kg-image-card"><img src="__GHOST_URL__/content/images/card.jpg" class="kg-image" alt loading="lazy"></figure>');
knexPost.plaintext.should.containEql('Testing [__GHOST_URL__/test]');
knexPost.plaintext.should.containEql('Testing');
knexPost.custom_excerpt.should.equal('Testing <a href="__GHOST_URL__/internal">links</a> in custom excerpts');
knexPost.codeinjection_head.should.equal('<script src="__GHOST_URL__/assets/head.js"></script>');
knexPost.codeinjection_foot.should.equal('<script src="__GHOST_URL__/assets/foot.js"></script>');

View File

@ -15,7 +15,7 @@ describe('Html to Plaintext', function () {
const {excerpt, email} = getEmailandExcert(input);
assert.equal(excerpt, 'Some thing Google [https://google.com] once told me.\n\nAnd another thing.');
assert.equal(excerpt, 'Some thing Google once told me.\n\nAnd another thing.');
assert.equal(email, 'Some thing Google [https://google.com] once told me.\n\nAnd another thing.');
});
@ -24,7 +24,7 @@ describe('Html to Plaintext', function () {
const {excerpt, email} = getEmailandExcert(input);
assert.equal(excerpt, 'A snippet from a post template\n\nSee? Not that scary! But still completely optional.');
assert.equal(excerpt, 'See? Not that scary! But still completely optional.');
assert.equal(email, 'A snippet from a post template\n\nSee? Not that scary! But still completely optional.');
});
@ -33,8 +33,24 @@ describe('Html to Plaintext', function () {
const {excerpt, email} = getEmailandExcert(input);
assert.equal(excerpt, 'A snippet from a post template [https://mysite.com]\n\nSee? Not that scary! But still completely optional.');
assert.equal(excerpt, 'See? Not that scary! But still completely optional.');
assert.equal(email, 'A snippet from a post template [https://mysite.com]\n\nSee? Not that scary! But still completely optional.');
});
it('longer example', function () {
const input = '<p>As discussed in the <a href="https://demo.ghost.io/welcome/">introduction</a> post, one of the best things about Ghost is just how much you can customize to turn your site into something unique. Everything about your layout and design can be changed, so you\'re not stuck with yet another clone of a social network profile.</p><p>How far you want to go with customization is completely up to you, there\'s no right or wrong approach! The majority of people use one of Ghost\'s built-in themes to get started, and then progress to something more bespoke later on as their site grows. </p><p>The best way to get started is with Ghost\'s branding settings, where you can set up colors, images and logos to fit with your brand.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://static.ghost.org/v4.0.0/images/brandsettings.png" class="kg-image" alt loading="lazy" width="3456" height="2338"><figcaption>Ghost Admin → Settings → Branding</figcaption></figure><p>Any Ghost theme that\'s up to date and compatible with Ghost 4.0 and higher will reflect your branding settings in the preview window, so you can see what your site will look like as you experiment with different options.</p><p>When selecting an accent color, try to choose something which will contrast well with white text. Many themes will use your accent color as the background for buttons, headers and navigational elements. Vibrant colors with a darker hue tend to work best, as a general rule.</p><h2 id="installing-ghost-themes">Installing Ghost themes</h2><p>By default, new sites are created with Ghost\'s friendly publication theme, called Casper. Everything in Casper is optimized to work for the most common types of blog, newsletter and publication that people create with Ghost — so it\'s a perfect place to start.</p><p>However, there are hundreds of different themes available to install, so you can pick out a look and feel that suits you best.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://static.ghost.org/v4.0.0/images/themesettings.png" class="kg-image" alt loading="lazy" width="3208" height="1618"><figcaption>Ghost Admin → Settings → Theme</figcaption></figure><p>Inside Ghost\'s theme settings you\'ll find 4 more official themes that can be directly installed and activated. Each theme is suited to slightly different use-cases.</p><ul><li><strong>Casper</strong> <em>(default)</em> — Made for all sorts of blogs and newsletters</li><li><strong>Edition</strong> — A beautiful minimal template for newsletter authors</li><li><strong>Alto</strong> — A slick news/magazine style design for creators</li><li><strong>London</strong> — A light photography theme with a bold grid</li><li><strong>Ease</strong> — A library theme for organizing large content archives</li></ul><p>And if none of those feel quite right, head on over to the <a href="https://ghost.org/themes/">Ghost Marketplace</a>, where you\'ll find a huge variety of both free and premium themes.</p><h2 id="building-something-custom">Building something custom</h2><p>Finally, if you want something completely bespoke for your site, you can always build a custom theme from scratch and upload it to your site.</p><p>Ghost\'s theming template files are very easy to work with, and can be picked up in the space of a few hours by anyone who has just a little bit of knowledge of HTML and CSS. Templates from other platforms can also be ported to Ghost with relatively little effort.</p><p>If you want to take a quick look at the theme syntax to see what it\'s like, you can <a href="https://github.com/tryghost/casper/">browse through the files of the default Casper theme</a>. We\'ve added tons of inline code comments to make it easy to learn, and the structure is very readable.</p><figure class="kg-card kg-code-card"><pre><code class="language-handlebars">{{#post}}\n&lt;article class="article {{post_class}}"&gt;\n\n &lt;h1&gt;{{title}}&lt;/h1&gt;\n \n {{#if feature_image}}\n \t&lt;img src="{{feature_image}}" alt="Feature image" /&gt;\n {{/if}}\n \n {{content}}\n\n&lt;/article&gt;\n{{/post}}</code></pre><figcaption>A snippet from a post template</figcaption></figure><p>See? Not that scary! But still completely optional. </p><p>If you\'re interested in creating your own Ghost theme, check out our extensive <a href="https://ghost.org/docs/themes/">theme documentation</a> for a full guide to all the different template variables and helpers which are available.</p>';
const {excerpt, email} = getEmailandExcert(input);
// No link
assert.doesNotMatch(excerpt, /https:\/\/demo\.ghost\.io\/welcome/);
// No figcaption
assert.doesNotMatch(excerpt, /Ghost Admin → Settings → Theme/);
// contains link
assert.match(email, /https:\/\/demo\.ghost\.io\/welcome/);
// contains figcaption
assert.match(email, /Ghost Admin → Settings → Theme/);
});
});
});