From 4ac75f44e87a84de154ced7f0f2f8a9e442ea331 Mon Sep 17 00:00:00 2001 From: Kumar McMillan Date: Mon, 26 Feb 2018 11:43:58 -0600 Subject: [PATCH] Deal with HTML entities when editing a collection (#4447) --- package.json | 1 + src/amo/components/CollectionManager/index.js | 7 +++++-- src/core/utils/index.js | 9 +++++++++ .../amo/components/TestCollectionManager.js | 19 +++++++++++++++++++ tests/unit/core/utils/test_index.js | 12 ++++++++++++ yarn.lock | 2 +- 6 files changed, 47 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 99eeba3828..9d413d5c82 100644 --- a/package.json +++ b/package.json @@ -167,6 +167,7 @@ "full-icu": "1.2.0", "helmet": "3.11.0", "hot-shots": "5.0.1", + "html-entities": "1.2.1", "humps": "2.0.1", "isomorphic-fetch": "2.2.1", "jed": "1.1.1", diff --git a/src/amo/components/CollectionManager/index.js b/src/amo/components/CollectionManager/index.js index 57a35c0549..ecc86f7b9e 100644 --- a/src/amo/components/CollectionManager/index.js +++ b/src/amo/components/CollectionManager/index.js @@ -7,6 +7,7 @@ import config from 'config'; import { updateCollection } from 'amo/reducers/collections'; import { withFixedErrorHandler } from 'core/errorHandler'; import translate from 'core/i18n/translate'; +import { decodeHtmlEntities } from 'core/utils'; import FormOverlay from 'ui/components/FormOverlay'; import type { CollectionType } from 'amo/reducers/collections'; import type { ApiStateType } from 'core/reducers/api'; @@ -107,9 +108,11 @@ export class CollectionManagerBase extends React.Component { } propsToState(props: Props) { + // Decode HTML entities so the user sees real symbols in the form. return { - description: props.collection && props.collection.description, - name: props.collection && props.collection.name, + description: props.collection && + decodeHtmlEntities(props.collection.description), + name: props.collection && decodeHtmlEntities(props.collection.name), slug: props.collection && props.collection.slug, }; } diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 782c04f3fb..5920a9a117 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -1,6 +1,7 @@ /* eslint-disable react/prop-types */ import url from 'url'; +import { AllHtmlEntities } from 'html-entities'; import config from 'config'; import * as React from 'react'; @@ -299,3 +300,11 @@ export function addonHasVersionHistory(addon) { ADDON_TYPE_THEME, ].includes(addon.type); } + +/* + * Decodes HTML entities into their respective symbols. + */ +export const decodeHtmlEntities = (string) => { + const entities = new AllHtmlEntities(); + return entities.decode(string); +}; diff --git a/tests/unit/amo/components/TestCollectionManager.js b/tests/unit/amo/components/TestCollectionManager.js index 099c17093b..aa5d540d89 100644 --- a/tests/unit/amo/components/TestCollectionManager.js +++ b/tests/unit/amo/components/TestCollectionManager.js @@ -8,6 +8,7 @@ import { } from 'amo/reducers/collections'; import { setLang } from 'core/actions'; import { setErrorMessage } from 'core/actions/errors'; +import { decodeHtmlEntities } from 'core/utils'; import { createFakeEvent, createStubErrorHandler, @@ -95,6 +96,24 @@ describe(__filename, () => { .toHaveProp('value', collection.slug); }); + it('strips HTML entities from name and description', () => { + const collection = createInternalCollection({ + detail: createFakeCollectionDetail({ + // This is how the API returns data. + name: 'Things & Stuff', + description: 'Extensions about things & stuff', + }), + }); + const root = render({ collection }); + + const { description, name } = collection; + + expect(root.find('#collectionName')) + .toHaveProp('value', decodeHtmlEntities(name)); + expect(root.find('#collectionDescription')) + .toHaveProp('defaultValue', decodeHtmlEntities(description)); + }); + it('does not populate form when updating to the same collection', () => { const firstCollection = createInternalCollection({ detail: createFakeCollectionDetail({ diff --git a/tests/unit/core/utils/test_index.js b/tests/unit/core/utils/test_index.js index 5f091766bd..dca60231a4 100644 --- a/tests/unit/core/utils/test_index.js +++ b/tests/unit/core/utils/test_index.js @@ -42,6 +42,7 @@ import { safePromise, sanitizeHTML, sanitizeUserHTML, + decodeHtmlEntities, visibleAddonType, trimAndAddProtocolToUrl, } from 'core/utils'; @@ -700,4 +701,15 @@ describe(__filename, () => { }); }); }); + + describe('decodeHtmlEntities', () => { + it('decodes entities', () => { + expect(decodeHtmlEntities('<>"&©®')) + .toEqual('<>"&©®'); + }); + + it('passes through anything else', () => { + expect(decodeHtmlEntities('just whatever')).toEqual('just whatever'); + }); + }); }); diff --git a/yarn.lock b/yarn.lock index 413cfc60e1..671ad12b02 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4176,7 +4176,7 @@ html-encoding-sniffer@^1.0.2: dependencies: whatwg-encoding "^1.0.1" -html-entities@^1.2.0: +html-entities@1.2.1, html-entities@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f"