diff --git a/news/288.bugfix b/news/288.bugfix new file mode 100644 index 00000000..2a88b713 --- /dev/null +++ b/news/288.bugfix @@ -0,0 +1 @@ +getVocabulary: Fix for terms with incomplete HTML diff --git a/plone/app/content/browser/vocabulary.py b/plone/app/content/browser/vocabulary.py index 7419e80c..8611163b 100644 --- a/plone/app/content/browser/vocabulary.py +++ b/plone/app/content/browser/vocabulary.py @@ -17,6 +17,7 @@ from Products.Five import BrowserView from Products.MimetypesRegistry.MimeTypeItem import guess_icon_path from Products.MimetypesRegistry.MimeTypeItem import PREFIX +from Products.PortalTransforms.transforms.safe_html import hasScript from Products.PortalTransforms.transforms.safe_html import SafeHTML from types import FunctionType from z3c.form.interfaces import IAddForm @@ -128,6 +129,12 @@ def get_translated_ignored(self): def get_base_path(self, context): return get_navigation_root(context) + def maybe_scrub(self, value): + if value and (hasScript(value) or "<" in value): + transform = SafeHTML() + return transform.scrub_html(value) + return value + def __call__(self): """ Accepts GET parameters of: @@ -210,7 +217,6 @@ def __call__(self): attributes = attributes.split(",") translate_ignored = self.get_translated_ignored() - transform = SafeHTML() if attributes: base_path = self.get_base_path(context) sm = getSecurityManager() @@ -261,8 +267,10 @@ def __call__(self): else: items = [ { - "id": item.value, - "text": (item.title if item.title else ""), + "id": unescape(self.maybe_scrub(item.value)), + "text": ( + unescape(self.maybe_scrub(item.title)) if item.title else "" + ), } for item in results ] @@ -270,9 +278,7 @@ def __call__(self): if total == 0: total = len(items) - return unescape( - transform.scrub_html(json_dumps({"results": items, "total": total})) - ) + return json_dumps({"results": items, "total": total}) def parsed_query( self, diff --git a/plone/app/content/tests/test_widgets.py b/plone/app/content/tests/test_widgets.py index 95e662dd..88469c13 100644 --- a/plone/app/content/tests/test_widgets.py +++ b/plone/app/content/tests/test_widgets.py @@ -680,6 +680,24 @@ def testGetMimeIcon(self): [{"getMimeIcon": "/plone/++resource++mimetype.icons/unknown.png"}], ) + def testScrubHtml(self): + from zope.schema.vocabulary import SimpleTerm + from zope.schema.vocabulary import SimpleVocabulary + + view = VocabularyView(self.portal, self.request) + vocab = SimpleVocabulary( + [ + SimpleTerm( + token=f"term {idx} ", + value=f"term {idx} ", + title=f"term {idx} ", + ) + for idx in range(3) + ] + ) + with mock.patch.object(view, "get_vocabulary", return_value=vocab): + json.loads(view()) + class FunctionalBrowserTest(unittest.TestCase): layer = PLONE_APP_CONTENT_DX_FUNCTIONAL_TESTING