mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-26 12:21:36 +03:00
Added {{readable_url}} helper (#18564)
refs https://github.com/TryGhost/Product/issues/4021 - used in the recommendations template to display urls in a short readable way, e.g. google.com instead of https://www.google.com?query=1#section
This commit is contained in:
parent
0ffe4dd144
commit
3cc60bc7ea
32
ghost/core/core/frontend/helpers/readable_url.js
Normal file
32
ghost/core/core/frontend/helpers/readable_url.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// # Readable URL helper
|
||||||
|
// Usage: `{{readable_url "https://google.com"}}`
|
||||||
|
//
|
||||||
|
// Returns a human readable URL for the given URL, e.g. google.com for https://www.google.com?query=1#section
|
||||||
|
|
||||||
|
const logging = require('@tryghost/logging');
|
||||||
|
const sentry = require('../../shared/sentry');
|
||||||
|
const errors = require('@tryghost/errors');
|
||||||
|
const {SafeString} = require('../services/handlebars');
|
||||||
|
|
||||||
|
function captureError(message) {
|
||||||
|
const error = new errors.IncorrectUsageError({message});
|
||||||
|
sentry.captureException(error);
|
||||||
|
logging.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = function readableUrl(inputUrl) {
|
||||||
|
if (!inputUrl || typeof inputUrl !== 'string') {
|
||||||
|
captureError(`Expected a string, received ${inputUrl}.`);
|
||||||
|
return new SafeString('');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const url = new URL(inputUrl);
|
||||||
|
const readable = url.hostname.replace(/^www\./, '') + url.pathname.replace(/\/$/, '');
|
||||||
|
|
||||||
|
return new SafeString(readable);
|
||||||
|
} catch (e) {
|
||||||
|
captureError(`The string "${inputUrl}" could not be parsed as URL.`);
|
||||||
|
return new SafeString(inputUrl);
|
||||||
|
}
|
||||||
|
};
|
@ -4,6 +4,7 @@
|
|||||||
<li class="recommendation">
|
<li class="recommendation">
|
||||||
<a href="{{rec.url}}" data-recommendation="{{rec.id}}" target="_blank" rel="noopener">
|
<a href="{{rec.url}}" data-recommendation="{{rec.id}}" target="_blank" rel="noopener">
|
||||||
<img class="recommendation-favicon" src="{{rec.favicon}}" alt="{{rec.title}}">
|
<img class="recommendation-favicon" src="{{rec.favicon}}" alt="{{rec.title}}">
|
||||||
|
<span class="recommendation-url">{{readable_url rec.url}}</span>
|
||||||
<h5 class="recommendation-title">{{rec.title}}</h5>
|
<h5 class="recommendation-title">{{rec.title}}</h5>
|
||||||
<p class="recommendation-description">{{rec.description}}</p>
|
<p class="recommendation-description">{{rec.description}}</p>
|
||||||
</a>
|
</a>
|
||||||
|
31
ghost/core/test/unit/frontend/helpers/readable_url.test.js
Normal file
31
ghost/core/test/unit/frontend/helpers/readable_url.test.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
const should = require('should');
|
||||||
|
const readable_url = require('../../../../core/frontend/helpers/readable_url');
|
||||||
|
|
||||||
|
describe('{{#readable_url}} helper', function () {
|
||||||
|
it('renders a short URL, without protocol, www, query params nor hash fragments', function () {
|
||||||
|
const readable = readable_url.call(
|
||||||
|
{},
|
||||||
|
'https://www.foobar.com/some/path/?query=param#hash/'
|
||||||
|
);
|
||||||
|
|
||||||
|
readable.string.should.equal('foobar.com/some/path');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders an empty string when the input is not a string', function () {
|
||||||
|
const readable = readable_url.call(
|
||||||
|
{},
|
||||||
|
{foo: 'bar'}
|
||||||
|
);
|
||||||
|
|
||||||
|
readable.string.should.equal('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the input string if not parsable as URL', function () {
|
||||||
|
const readable = readable_url.call(
|
||||||
|
{},
|
||||||
|
'hello world'
|
||||||
|
);
|
||||||
|
|
||||||
|
readable.string.should.equal('hello world');
|
||||||
|
});
|
||||||
|
});
|
@ -10,6 +10,7 @@ const proxy = require('../../../../core/frontend/services/proxy');
|
|||||||
|
|
||||||
const recommendations = require('../../../../core/frontend/helpers/recommendations');
|
const recommendations = require('../../../../core/frontend/helpers/recommendations');
|
||||||
const foreach = require('../../../../core/frontend/helpers/foreach');
|
const foreach = require('../../../../core/frontend/helpers/foreach');
|
||||||
|
const readable_url = require('../../../../core/frontend/helpers/readable_url');
|
||||||
const {settingsCache} = proxy;
|
const {settingsCache} = proxy;
|
||||||
|
|
||||||
function trimSpaces(string) {
|
function trimSpaces(string) {
|
||||||
@ -30,6 +31,7 @@ describe('{{#recommendations}} helper', function () {
|
|||||||
|
|
||||||
// The recommendation template expects this helper
|
// The recommendation template expects this helper
|
||||||
hbs.registerHelper('foreach', foreach);
|
hbs.registerHelper('foreach', foreach);
|
||||||
|
hbs.registerHelper('readable_url', readable_url);
|
||||||
|
|
||||||
// Stub settings cache
|
// Stub settings cache
|
||||||
sinon.stub(settingsCache, 'get');
|
sinon.stub(settingsCache, 'get');
|
||||||
@ -42,10 +44,7 @@ describe('{{#recommendations}} helper', function () {
|
|||||||
return {
|
return {
|
||||||
browse: sinon.stub().resolves({recommendations: [
|
browse: sinon.stub().resolves({recommendations: [
|
||||||
{id: '1', title: 'Recommendation 1', url: 'https://recommendations1.com', favicon: 'https://recommendations1.com/favicon.ico', description: 'Description 1'},
|
{id: '1', title: 'Recommendation 1', url: 'https://recommendations1.com', favicon: 'https://recommendations1.com/favicon.ico', description: 'Description 1'},
|
||||||
{id: '2', title: 'Recommendation 2', url: 'https://recommendations2.com', favicon: 'https://recommendations2.com/favicon.ico', description: 'Description 2'},
|
{id: '2', title: 'Recommendation 2', url: 'https://recommendations2.com', favicon: 'https://recommendations2.com/favicon.ico', description: 'Description 2'}
|
||||||
{id: '3', title: 'Recommendation 3', url: 'https://recommendations3.com', favicon: 'https://recommendations3.com/favicon.ico', description: 'Description 3'},
|
|
||||||
{id: '4', title: 'Recommendation 4', url: 'https://recommendations4.com', favicon: 'https://recommendations4.com/favicon.ico', description: 'Description 4'},
|
|
||||||
{id: '5', title: 'Recommendation 5', url: 'https://recommendations5.com', favicon: 'https://recommendations5.com/favicon.ico', description: 'Description 5'}
|
|
||||||
], meta: meta})
|
], meta: meta})
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -73,6 +72,7 @@ describe('{{#recommendations}} helper', function () {
|
|||||||
<li class="recommendation">
|
<li class="recommendation">
|
||||||
<a href="https://recommendations1.com" data-recommendation="1" target="_blank" rel="noopener">
|
<a href="https://recommendations1.com" data-recommendation="1" target="_blank" rel="noopener">
|
||||||
<img class="recommendation-favicon" src="https://recommendations1.com/favicon.ico" alt="Recommendation 1">
|
<img class="recommendation-favicon" src="https://recommendations1.com/favicon.ico" alt="Recommendation 1">
|
||||||
|
<span class="recommendation-url">recommendations1.com</span>
|
||||||
<h5 class="recommendation-title">Recommendation 1</h5>
|
<h5 class="recommendation-title">Recommendation 1</h5>
|
||||||
<p class="recommendation-description">Description 1</p>
|
<p class="recommendation-description">Description 1</p>
|
||||||
</a>
|
</a>
|
||||||
@ -80,31 +80,11 @@ describe('{{#recommendations}} helper', function () {
|
|||||||
<li class="recommendation">
|
<li class="recommendation">
|
||||||
<a href="https://recommendations2.com" data-recommendation="2" target="_blank" rel="noopener">
|
<a href="https://recommendations2.com" data-recommendation="2" target="_blank" rel="noopener">
|
||||||
<img class="recommendation-favicon" src="https://recommendations2.com/favicon.ico" alt="Recommendation 2">
|
<img class="recommendation-favicon" src="https://recommendations2.com/favicon.ico" alt="Recommendation 2">
|
||||||
|
<span class="recommendation-url">recommendations2.com</span>
|
||||||
<h5 class="recommendation-title">Recommendation 2</h5>
|
<h5 class="recommendation-title">Recommendation 2</h5>
|
||||||
<p class="recommendation-description">Description 2</p>
|
<p class="recommendation-description">Description 2</p>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="recommendation">
|
|
||||||
<a href="https://recommendations3.com" data-recommendation="3" target="_blank" rel="noopener">
|
|
||||||
<img class="recommendation-favicon" src="https://recommendations3.com/favicon.ico" alt="Recommendation 3">
|
|
||||||
<h5 class="recommendation-title">Recommendation 3</h5>
|
|
||||||
<p class="recommendation-description">Description 3</p>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="recommendation">
|
|
||||||
<a href="https://recommendations4.com" data-recommendation="4" target="_blank" rel="noopener">
|
|
||||||
<img class="recommendation-favicon" src="https://recommendations4.com/favicon.ico" alt="Recommendation 4">
|
|
||||||
<h5 class="recommendation-title">Recommendation 4</h5>
|
|
||||||
<p class="recommendation-description">Description 4</p>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="recommendation">
|
|
||||||
<a href="https://recommendations5.com" data-recommendation="5" target="_blank" rel="noopener">
|
|
||||||
<img class="recommendation-favicon" src="https://recommendations5.com/favicon.ico" alt="Recommendation 5">
|
|
||||||
<h5 class="recommendation-title">Recommendation 5</h5>
|
|
||||||
<p class="recommendation-description">Description 5</p>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
`);
|
`);
|
||||||
const actual = trimSpaces(response.string);
|
const actual = trimSpaces(response.string);
|
||||||
|
@ -11,7 +11,7 @@ describe('Helpers', function () {
|
|||||||
'asset', 'authors', 'body_class', 'cancel_link', 'concat', 'content', 'date', 'encode', 'excerpt', 'facebook_url', 'foreach', 'get',
|
'asset', 'authors', 'body_class', 'cancel_link', 'concat', 'content', 'date', 'encode', 'excerpt', 'facebook_url', 'foreach', 'get',
|
||||||
'ghost_foot', 'ghost_head', 'has', 'img_url', 'is', 'link', 'link_class', 'meta_description', 'meta_title', 'navigation',
|
'ghost_foot', 'ghost_head', 'has', 'img_url', 'is', 'link', 'link_class', 'meta_description', 'meta_title', 'navigation',
|
||||||
'next_post', 'page_url', 'pagination', 'plural', 'post_class', 'prev_post', 'price', 'raw', 'reading_time', 't', 'tags', 'title','total_members', 'total_paid_members', 'twitter_url',
|
'next_post', 'page_url', 'pagination', 'plural', 'post_class', 'prev_post', 'price', 'raw', 'reading_time', 't', 'tags', 'title','total_members', 'total_paid_members', 'twitter_url',
|
||||||
'url', 'comment_count', 'collection', 'recommendations'
|
'url', 'comment_count', 'collection', 'recommendations', 'readable_url'
|
||||||
];
|
];
|
||||||
const experimentalHelpers = ['match', 'tiers', 'comments', 'search'];
|
const experimentalHelpers = ['match', 'tiers', 'comments', 'search'];
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user