0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-24 23:48:13 -05:00

🐛 Fixed navigation url inputs when configured URL has special characters (#941)

closes https://github.com/TryGhost/Ghost/issues/9373

- using an `<a>` element to parse a URL does not behave as expected when the URL has special characters because the `host` attribute will show the Puny URL version. Eg. `exämple.com` will become `xn--exmple-cua.com`
- `{{gh-navitem-url-input}}` was failing to manipulate the URL value because of the difference between the Puny URL encoded URL and the raw configured URL with unicode chars
- uses the `URI` module that's bundled with the imported version of `google-caja` to parse the URL via regexes rather than relying on native browser parsing
This commit is contained in:
Kevin Ansfield 2018-01-07 11:14:24 +00:00 committed by Katharina Irrgang
parent 85d4759044
commit cc33fa899d
3 changed files with 48 additions and 15 deletions

View file

@ -3,6 +3,10 @@ import {InvokeActionMixin} from 'ember-invoke-action';
import {computed} from '@ember/object'; import {computed} from '@ember/object';
import {run} from '@ember/runloop'; import {run} from '@ember/runloop';
// URI is attached to the window global as part of the
// google-caja html-css-sanitizer-bundle
const {URI} = window;
let joinUrlParts = function (url, path) { let joinUrlParts = function (url, path) {
if (path[0] !== '/' && url.slice(-1) !== '/') { if (path[0] !== '/' && url.slice(-1) !== '/') {
path = `/${path}`; path = `/${path}`;
@ -86,17 +90,26 @@ export default TextField.extend(InvokeActionMixin, {
notifyUrlChanged() { notifyUrlChanged() {
let url = this.get('value').trim(); let url = this.get('value').trim();
let urlParts = document.createElement('a'); let urlURI = URI.parse(url);
let baseUrl = this.get('baseUrl'); let baseUrl = this.get('baseUrl');
let baseUrlParts = document.createElement('a'); let baseURI = URI.parse(baseUrl);
function getHost(uri) {
let host = uri.getDomain();
if (uri.getPort()) {
host = `${host}:${uri.getPort()}`;
}
return host;
}
let urlHost = getHost(urlURI);
let baseHost = getHost(baseURI);
// ensure value property is trimmed // ensure value property is trimmed
this.set('value', url); this.set('value', url);
// leverage the browser's native URI parsing
urlParts.href = url;
baseUrlParts.href = baseUrl;
// if we have an email address, add the mailto: // if we have an email address, add the mailto:
if (validator.isEmail(url)) { if (validator.isEmail(url)) {
url = `mailto:${url}`; url = `mailto:${url}`;
@ -110,12 +123,12 @@ export default TextField.extend(InvokeActionMixin, {
} }
// get our baseUrl relativity checks in order // get our baseUrl relativity checks in order
let isOnSameHost = urlParts.host === baseUrlParts.host; let isOnSameHost = urlHost === baseHost;
let isAnchorLink = url.match(/^#/); let isAnchorLink = url.match(/^#/);
let isRelativeToBasePath = urlParts.pathname.indexOf(baseUrlParts.pathname) === 0; let isRelativeToBasePath = urlURI.getPath() && urlURI.getPath().indexOf(baseURI.getPath()) === 0;
// if our pathname is only missing a trailing / mark it as relative // if our path is only missing a trailing / mark it as relative
if (`${urlParts.pathname}/` === baseUrlParts.pathname) { if (`${urlURI.getPath()}/` === baseURI.getPath()) {
isRelativeToBasePath = true; isRelativeToBasePath = true;
} }
@ -123,12 +136,12 @@ export default TextField.extend(InvokeActionMixin, {
if (!isAnchorLink && isOnSameHost && isRelativeToBasePath) { if (!isAnchorLink && isOnSameHost && isRelativeToBasePath) {
url = url.replace(/^[a-zA-Z0-9-]+:/, ''); url = url.replace(/^[a-zA-Z0-9-]+:/, '');
url = url.replace(/^\/\//, ''); url = url.replace(/^\/\//, '');
url = url.replace(baseUrlParts.host, ''); url = url.replace(baseHost, '');
url = url.replace(baseUrlParts.pathname, ''); url = url.replace(baseURI.getPath(), '');
// handle case where url path is same as baseUrl path but missing trailing slash // handle case where url path is same as baseUrl path but missing trailing slash
if (urlParts.pathname.slice(-1) !== '/') { if (urlURI.getPath().slice(-1) !== '/') {
url = url.replace(baseUrlParts.pathname.slice(0, -1), ''); url = url.replace(baseURI.getPath().slice(0, -1), '');
} }
if (url !== '' || !this.get('isNew')) { if (url !== '' || !this.get('isNew')) {

View file

@ -157,7 +157,7 @@ describe('Acceptance: Settings - Design', function () {
expect( expect(
find('.gh-blognav-url:last input').val() find('.gh-blognav-url:last input').val()
).to.equal(`${window.location.protocol}//${window.location.host}/new/`); ).to.equal(`${window.location.protocol}//${window.location.host}/new`);
await click('.gh-blognav-add'); await click('.gh-blognav-add');

View file

@ -187,6 +187,26 @@ describe('Integration: Component: gh-navitem-url-input', function () {
expect($input.val()).to.equal(`${currentUrl} /test`); expect($input.val()).to.equal(`${currentUrl} /test`);
}); });
// https://github.com/TryGhost/Ghost/issues/9373
it('doesn\'t mangle urls when baseUrl has unicode characters', function () {
this.on('updateUrl', () => {
return null;
});
this.set('baseUrl', 'http://exämple.com');
this.render(hbs`
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action "updateUrl") clearErrors=(action "clearErrors")}}
`);
let $input = this.$('input');
run(() => {
$input.val(`${currentUrl}/test`).trigger('input').trigger('blur');
});
expect($input.val()).to.equal(`${currentUrl}/test`);
});
it('triggers "update" action on blur', function () { it('triggers "update" action on blur', function () {
let changeActionCallCount = 0; let changeActionCallCount = 0;
this.on('updateUrl', () => { this.on('updateUrl', () => {