🐛 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 4baa57be56
commit bd83aab585
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 {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) {
if (path[0] !== '/' && url.slice(-1) !== '/') {
path = `/${path}`;
@ -86,17 +90,26 @@ export default TextField.extend(InvokeActionMixin, {
notifyUrlChanged() {
let url = this.get('value').trim();
let urlParts = document.createElement('a');
let urlURI = URI.parse(url);
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
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 (validator.isEmail(url)) {
url = `mailto:${url}`;
@ -110,12 +123,12 @@ export default TextField.extend(InvokeActionMixin, {
}
// get our baseUrl relativity checks in order
let isOnSameHost = urlParts.host === baseUrlParts.host;
let isOnSameHost = urlHost === baseHost;
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 (`${urlParts.pathname}/` === baseUrlParts.pathname) {
// if our path is only missing a trailing / mark it as relative
if (`${urlURI.getPath()}/` === baseURI.getPath()) {
isRelativeToBasePath = true;
}
@ -123,12 +136,12 @@ export default TextField.extend(InvokeActionMixin, {
if (!isAnchorLink && isOnSameHost && isRelativeToBasePath) {
url = url.replace(/^[a-zA-Z0-9-]+:/, '');
url = url.replace(/^\/\//, '');
url = url.replace(baseUrlParts.host, '');
url = url.replace(baseUrlParts.pathname, '');
url = url.replace(baseHost, '');
url = url.replace(baseURI.getPath(), '');
// handle case where url path is same as baseUrl path but missing trailing slash
if (urlParts.pathname.slice(-1) !== '/') {
url = url.replace(baseUrlParts.pathname.slice(0, -1), '');
if (urlURI.getPath().slice(-1) !== '/') {
url = url.replace(baseURI.getPath().slice(0, -1), '');
}
if (url !== '' || !this.get('isNew')) {

View File

@ -157,7 +157,7 @@ describe('Acceptance: Settings - Design', function () {
expect(
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');

View File

@ -187,6 +187,26 @@ describe('Integration: Component: gh-navitem-url-input', function () {
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 () {
let changeActionCallCount = 0;
this.on('updateUrl', () => {