Products.CMFPlone/robodoc: Merge branch 'master' into robodoc
GitHub <jenkins-z4DKO/[email protected]> Fri, 28 Jul 2017 05:39:18 -0700 (PDT)
| Newsgroups | gmane.comp.web.zope.plone.cvs |
|---|---|
| Message-ID | <[email protected]> |
Repository: Products.CMFPlone Branch: refs/heads/robodoc Date: 2017-07-28T14:38:51+02:00 Author: Paul Roeland (polyester) <paul-4+yus/[email protected]> Commit: https://github.com/plone/Products.CMFPlone/commit/effcfe7481d049a6727ccbc3bf08e80dbdbe63f4 Merge branch 'master' into robodoc Files changed: M CHANGES.rst M Products/CMFPlone/controlpanel/bbb/filter.py M Products/CMFPlone/controlpanel/browser/filter.py M Products/CMFPlone/controlpanel/tests/test_controlpanel_bbb_filter_adapter.py M Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_filter.py M Products/CMFPlone/controlpanel/tests/test_controlpanel_filter.py M Products/CMFPlone/interfaces/controlpanel.py M Products/CMFPlone/patterns/settings.py M Products/CMFPlone/patterns/tinymce.py M Products/CMFPlone/tests/robot/test_controlpanel_filter.robot M Products/CMFPlone/tests/testPortalCreation.py M Products/CMFPlone/tests/test_z3c_form_widgets.py M setup.py diff --git a/CHANGES.rst b/CHANGES.rst index 325e2b1ba..9ba995af6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -15,6 +15,9 @@ Breaking changes: Introduced PyScss as a drop in replacement that could also do more things. Discussion on that at https://github.com/plone/Products.CMFPlone/issues/1800 [loechel] +- Fix and migrate safe_html filter completly into Plone registry and sync settings with TinyMCE. + Also some unused options in controlpanel where removed, like stripped_combinations and class_blacklist. + [MrTango] New features: diff --git a/Products/CMFPlone/controlpanel/bbb/filter.py b/Products/CMFPlone/controlpanel/bbb/filter.py index a8ea0b1c6..ed40da042 100644 --- a/Products/CMFPlone/controlpanel/bbb/filter.py +++ b/Products/CMFPlone/controlpanel/bbb/filter.py @@ -28,49 +28,20 @@ def get_nasty_tags(self): def set_nasty_tags(self, value): self.settings.nasty_tags = value - def get_stripped_tags(self): - return self.settings.stripped_tags + def get_valid_tags(self): + return self.settings.valid_tags - def set_stripped_tags(self, value): - self.settings.stripped_tags = value + def set_valid_tags(self, value): + self.settings.valid_tags = value - def get_custom_tags(self): - return self.settings.custom_tags + def get_custom_attributes(self): + return self.settings.custom_attributes - def set_custom_tags(self, value): - self.settings.custom_tags = value + def set_custom_attributes(self, value): + self.settings.custom_attributes = value - def get_stripped_attributes(self): - return self.settings.stripped_attributes - - def set_stripped_attributes(self, value): - self.settings.stripped_attributes = value - - def get_stripped_combinations(self): - return self.settings.stripped_combinations - - def set_stripped_combinations(self, value): - self.settings.stripped_combinations = value - - def get_style_whitelist(self): - return self.settings.style_whitelist - - def set_style_whitelist(self, value): - self.settings.style_whitelist = value - - def get_class_blacklist(self): - return self.settings.class_blacklist - - def set_class_blacklist(self, value): - self.settings.class_blacklist = value - - class_blacklist = property(get_class_blacklist, set_class_blacklist) - style_whitelist = property(get_style_whitelist, set_style_whitelist) - stripped_combinations = property(get_stripped_combinations, - set_stripped_combinations) - stripped_attributes = property(get_stripped_attributes, - set_stripped_attributes) - custom_tags = property(get_custom_tags, set_custom_tags) - stripped_tags = property(get_stripped_tags, set_stripped_tags) + custom_attributes = property( + get_custom_attributes, set_custom_attributes) + valid_tags = property(get_valid_tags, set_valid_tags) nasty_tags = property(get_nasty_tags, set_nasty_tags) disable_filtering = property(get_disable_filtering, set_disable_filtering) diff --git a/Products/CMFPlone/controlpanel/browser/filter.py b/Products/CMFPlone/controlpanel/browser/filter.py index 7a479dae3..8c822d855 100644 --- a/Products/CMFPlone/controlpanel/browser/filter.py +++ b/Products/CMFPlone/controlpanel/browser/filter.py @@ -1,31 +1,23 @@ # -*- coding: utf-8 -*- -from Products.CMFCore.utils import getToolByName +from plone.app.registry.browser import controlpanel +from plone.z3cform import layout from Products.CMFPlone import PloneMessageFactory as _ # NOQA from Products.CMFPlone.interfaces import IFilterSchema from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile from Products.statusmessages.interfaces import IStatusMessage -from plone.autoform.form import AutoExtensibleForm -from plone.z3cform import layout from z3c.form import button -from z3c.form import form -from Products.PortalTransforms.transforms.safe_html import VALID_TAGS -from Products.PortalTransforms.transforms.safe_html import NASTY_TAGS -class FilterControlPanel(AutoExtensibleForm, form.EditForm): +class FilterControlPanel(controlpanel.RegistryEditForm): id = "FilterControlPanel" label = _(u"HTML Filtering Settings") - description = "" + description = _("Keep in mind that editors like TinyMCE might have " + "additional filters.") schema = IFilterSchema + schema_prefix = "plone" form_name = _(u"HTML Filtering Settings") control_panel_view = "filter-controlpanel" - def updateActions(self): # NOQA - """Have to override this because we only have Save, not Cancel - """ - super(FilterControlPanel, self).updateActions() - self.actions['save'].addClass("context") - @button.buttonAndHandler(_(u"Save"), name='save') def handleSave(self, action): # NOQA data, errors = self.extractData() @@ -33,54 +25,6 @@ def handleSave(self, action): # NOQA self.status = self.formErrorsMessage return - # Save in portal tools - safe_html = getattr( - getToolByName(self.context, 'portal_transforms'), - 'safe_html', - None) - - nasty_tags = data['nasty_tags'] - custom_tags = data['custom_tags'] - stripped_tags = data['stripped_tags'] - - valid = safe_html._config['valid_tags'] - - # start with removing tags that do not belong in valid tags - for value in nasty_tags + stripped_tags: - if value in valid: - del valid[value] - # add in custom tags - for custom in custom_tags: - if value not in valid: - valid[custom] = 1 - # then, check if something was previously prevented but is no longer - for tag in set(VALID_TAGS.keys()) - set(valid.keys()): - if tag not in nasty_tags and tag not in stripped_tags: - valid[tag] = VALID_TAGS[tag] - - # nasty tags are simple, just set the value here - nasty_value = {tag: NASTY_TAGS.get(tag, VALID_TAGS.get(tag, 1)) for tag in nasty_tags} - safe_html._config['nasty_tags'] = nasty_value - - disable_filtering = int(data['disable_filtering']) - if disable_filtering != safe_html._config['disable_transform']: - safe_html._config['disable_transform'] = disable_filtering - - for attr in ('stripped_combinations', 'class_blacklist', - 'stripped_attributes', 'style_whitelist'): - value = data[attr] - if value is None: - if attr == 'stripped_combinations': - value = {} - else: - value = [] - if value != safe_html._config[attr]: - safe_html._config[attr] = value - - # always reload the transform - safe_html._p_changed = True - safe_html.reload() - self.applyChanges(data) IStatusMessage(self.request).addStatusMessage( _(u"Changes saved."), @@ -92,6 +36,15 @@ def handleSave(self, action): # NOQA "warning") self.request.response.redirect(self.request.getURL()) + @button.buttonAndHandler(_(u"Cancel"), name='cancel') + def handleCancel(self, action): + IStatusMessage(self.request).addStatusMessage( + _(u"Changes canceled."), + "info") + self.request.response.redirect("%s/%s" % ( + self.context.absolute_url(), + self.control_panel_view)) + class ControlPanelFormWrapper(layout.FormWrapper): """Use this form as the plone.z3cform layout wrapper to get the control diff --git a/Products/CMFPlone/controlpanel/tests/test_controlpanel_bbb_filter_adapter.py b/Products/CMFPlone/controlpanel/tests/test_controlpanel_bbb_filter_adapter.py index 45166579c..523d1271c 100644 --- a/Products/CMFPlone/controlpanel/tests/test_controlpanel_bbb_filter_adapter.py +++ b/Products/CMFPlone/controlpanel/tests/test_controlpanel_bbb_filter_adapter.py @@ -37,78 +37,32 @@ def test_set_nasty_tags(self): [u'foo', u'bar'] ) - def test_get_stripped_tags(self): - self.settings.stripped_tags = [u'foo', u'bar'] + def test_get_valid_tags(self): + self.settings.valid_tags = [u'foo', u'bar'] self.assertEquals( - getAdapter(self.portal, IFilterSchema).stripped_tags, + getAdapter(self.portal, IFilterSchema).valid_tags, [u'foo', u'bar'] ) - def test_set_stripped_tags(self): - getAdapter(self.portal, IFilterSchema).stripped_tags = [u'foo', u'bar'] + def test_set_valid_tags(self): + getAdapter(self.portal, IFilterSchema).valid_tags = [u'foo', u'bar'] self.assertEquals( - self.settings.stripped_tags, + self.settings.valid_tags, [u'foo', u'bar'] ) - def test_get_custom_tags(self): - self.settings.custom_tags = [u'foo', u'bar'] + def test_get_custom_attributes(self): + self.settings.custom_attributes = [u'foo', u'bar'] self.assertEquals( - getAdapter(self.portal, IFilterSchema).custom_tags, + getAdapter(self.portal, IFilterSchema).custom_attributes, [u'foo', u'bar'] ) - def test_set_custom_tags(self): - getAdapter(self.portal, IFilterSchema).custom_tags = [u'foo', u'bar'] - self.assertEquals( - self.settings.custom_tags, - [u'foo', u'bar'] - ) - - def test_get_stripped_attributes(self): - self.settings.stripped_attributes = [u'foo', u'bar'] - self.assertEquals( - getAdapter(self.portal, IFilterSchema).stripped_attributes, - [u'foo', u'bar'] - ) - - def test_set_stripped_attributes(self): - getAdapter(self.portal, IFilterSchema).stripped_attributes = [ - u'foo', u'bar' - ] - self.assertEquals( - self.settings.stripped_attributes, - [u'foo', u'bar'] - ) - - def test_get_style_whitelist(self): - self.settings.style_whitelist = [u'foo', u'bar'] - self.assertEquals( - getAdapter(self.portal, IFilterSchema).style_whitelist, - [u'foo', u'bar'] - ) - - def test_set_style_whitelist(self): - getAdapter(self.portal, IFilterSchema).style_whitelist = [ - u'foo', u'bar' - ] - self.assertEquals( - self.settings.style_whitelist, - [u'foo', u'bar'] - ) - - def test_get_class_blacklist(self): - self.settings.class_blacklist = [u'foo', u'bar'] - self.assertEquals( - getAdapter(self.portal, IFilterSchema).class_blacklist, - [u'foo', u'bar'] - ) - - def test_set_class_blacklist(self): - getAdapter(self.portal, IFilterSchema).class_blacklist = [ + def test_set_custom_attributes(self): + getAdapter(self.portal, IFilterSchema).custom_attributes = [ u'foo', u'bar' ] self.assertEquals( - self.settings.class_blacklist, + self.settings.custom_attributes, [u'foo', u'bar'] ) diff --git a/Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_filter.py b/Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_filter.py index 09bff81d0..5769032b9 100644 --- a/Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_filter.py +++ b/Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_filter.py @@ -1,11 +1,13 @@ # -*- coding: utf-8 -*- +from plone.app.testing import SITE_OWNER_NAME, SITE_OWNER_PASSWORD +from plone.registry.interfaces import IRegistry +from plone.testing.z2 import Browser from Products.CMFCore.utils import getToolByName -# from Products.CMFPlone.interfaces import IFilterSchema +from Products.CMFPlone.interfaces import IFilterSchema from Products.CMFPlone.testing import PRODUCTS_CMFPLONE_FUNCTIONAL_TESTING from Products.PortalTransforms.data import datastream -from plone.app.testing import SITE_OWNER_NAME, SITE_OWNER_PASSWORD -from plone.testing.z2 import Browser from zope.component import getMultiAdapter +from zope.component import getUtility import unittest @@ -21,6 +23,9 @@ def setUp(self): self.portal = self.layer['portal'] self.request = self.layer['request'] self.portal_url = self.portal.absolute_url() + registry = getUtility(IRegistry) + self.settings = registry.forInterface( + IFilterSchema, prefix="plone") self.browser = Browser(self.app) self.browser.handleErrors = False self.browser.addHeader( @@ -64,7 +69,7 @@ def test_disable_filtering(self): # test that the transform is disabled self.assertEqual( - self.safe_html._config['disable_transform'], + self.settings.disable_filtering, 1) # anything passes @@ -81,49 +86,18 @@ def test_nasty_tags(self): self.browser.getControl( name='form.widgets.nasty_tags' ).value = 'div\r\na' + valid_tags = self.browser.getControl( + name='form.widgets.valid_tags').value + valid_tags = valid_tags.replace('a\r\n', '') + valid_tags = self.browser.getControl( + name='form.widgets.valid_tags').value = valid_tags self.browser.getControl('Save').click() # test that <a> is filtered - self.assertFalse(self.safe_html._config['disable_transform']) + self.assertFalse(self.settings.disable_filtering) good_html = '<p><a href="http://example.com">harmless link</a></p>' ds = datastream('dummy_name') self.assertEqual( str(self.safe_html.convert(good_html, ds)), - '' + '<p/>' ) - - @unittest.skip('This functionality was broken with formlib already. Needs fix.') # noqa - def test_stripped_combinations(self): - # test a combination that isn't normally filtered - self.assertFalse(self.safe_html._config['disable_transform']) - html = '<p class="wow">lala</p>' - ds = datastream('dummy_name') - self.assertEqual( - str(self.safe_html.convert(html, ds)), - html) - - # we can set stripped combinations - self.browser.open( - "%s/@@filter-controlpanel" % self.portal_url) - self.browser.getControl( - name='form.widgets.stripped_combinations.buttons.add').click() - self.browser.getControl( - name='form.widgets.stripped_combinations.key.0' - ).value = 'mytag1 p' - self.browser.getControl( - name='form.widgets.stripped_combinations.0' - ).value = 'myattr1 class' - self.browser.getControl('Save').click() - - # stripped combinations are stored on the transform - self.assertIn( - 'mytag1 p', - self.safe_html._config['stripped_combinations']) - self.assertEqual( - 'myattr1 class', - self.safe_html._config['stripped_combinations']['mytag1 p']) - - # test that combination is now filtered - self.assertEqual( - str(self.safe_html.convert(html, ds)), - '<p>lala</p>') diff --git a/Products/CMFPlone/controlpanel/tests/test_controlpanel_filter.py b/Products/CMFPlone/controlpanel/tests/test_controlpanel_filter.py index df8c35da6..fd5d2f016 100644 --- a/Products/CMFPlone/controlpanel/tests/test_controlpanel_filter.py +++ b/Products/CMFPlone/controlpanel/tests/test_controlpanel_filter.py @@ -40,20 +40,8 @@ def test_disable_filtering_setting(self): def test_nasty_tags_setting(self): self.assertTrue(hasattr(self.settings, 'nasty_tags')) - def test_stripped_tags_setting(self): - self.assertTrue(hasattr(self.settings, 'stripped_tags')) + def test_valid_tags_setting(self): + self.assertTrue(hasattr(self.settings, 'valid_tags')) - def test_custom_tags_setting(self): - self.assertTrue(hasattr(self.settings, 'custom_tags')) - - def test_stripped_attributes_setting(self): - self.assertTrue(hasattr(self.settings, 'stripped_attributes')) - - # def test_stripped_combinations_setting(self): - # self.assertTrue(hasattr(self.settings, 'stripped_combinations')) - - def test_style_whitelist_setting(self): - self.assertTrue(hasattr(self.settings, 'style_whitelist')) - - def test_class_blacklist_setting(self): - self.assertTrue(hasattr(self.settings, 'class_blacklist')) + def test_custom_attributes_setting(self): + self.assertTrue(hasattr(self.settings, 'custom_attributes')) diff --git a/Products/CMFPlone/interfaces/controlpanel.py b/Products/CMFPlone/interfaces/controlpanel.py index e6526b644..58a811fed 100644 --- a/Products/CMFPlone/interfaces/controlpanel.py +++ b/Products/CMFPlone/interfaces/controlpanel.py @@ -355,75 +355,112 @@ class IFilterSchema(Interface): nasty_tags = schema.List( title=_(u'Nasty tags'), description=_(u'These tags and their content are completely blocked ' - 'when a page is saved or rendered.'), + 'when a page is saved or rendered. They are only deleted' + ' if they are not marked as valid_tags'), default=[u'style', u'object', u'embed', u'applet', u'script', u'meta'], value_type=schema.TextLine(), missing_value=[], required=False) - stripped_tags = schema.List( - title=_(u'Stripped tags'), - description=_(u'These tags are stripped when saving or rendering, ' - 'but any content is preserved.'), - default=[u'font', ], - value_type=schema.TextLine(), - missing_value=[], - required=False) - - custom_tags = schema.List( - title=_(u'Custom tags'), - description=_(u'Add tag names here for tags which are not part of ' - 'XHTML but which should be permitted.'), - default=[], - value_type=schema.TextLine(), - missing_value=[], - required=False) - - # class IFilterAttributesSchema(Interface): - - stripped_attributes = schema.List( - title=_(u'Stripped attributes'), - description=_(u'These attributes are stripped from any tag when ' - 'saving.'), - default=(u'dir lang valign halign border frame rules cellspacing ' - 'cellpadding bgcolor').split(), - value_type=schema.TextLine(), - missing_value=[], - required=False) - - stripped_combinations = schema.Dict( - title=_(u'Stripped combinations'), - description=_(u'These attributes are stripped from those tags when ' - 'saving.'), - key_type=schema.TextLine(title=u'tags'), - value_type=schema.TextLine(title=u'attributes'), - default={'table th td': 'width height'}, - missing_value={}, - required=False) - - # class IFilterEditorSchema(Interface): - - style_whitelist = schema.List( - title=_(u'Permitted properties'), - description=_( - u'These CSS properties are allowed in style attributes.'), + valid_tags = schema.List( + title=_(u'Valid tags'), + description=_(u'A list of valid tags which will be not filtered out.'), default=[ - u'text-align', - u'list-style-type', - u'float padding-left', - u'text-decoration' + u'a', + u'abbr', + u'acronym', + u'address', + u'article', + u'aside', + u'audio', + u'b', + u'bdo', + u'big', + u'blockquote', + u'body', + u'br', + u'canvas', + u'caption', + u'cite', + u'code', + u'col', + u'colgroup', + u'command', + u'datalist', + u'dd', + u'del', + u'details', + u'dfn', + u'dialog', + u'div', + u'dl', + u'dt', + u'em', + u'figure', + u'footer', + u'h1', + u'h2', + u'h3', + u'h4', + u'h5', + u'h6', + u'head', + u'header', + u'hgroup', + u'html', + u'i', + u'iframe', + u'img', + u'ins', + u'kbd', + u'keygen', + u'li', + u'map', + u'mark', + u'meter', + u'nav', + u'ol', + u'output', + u'p', + u'pre', + u'progress', + u'q', + u'rp', + u'rt', + u'ruby', + u'samp', + u'section', + u'small', + u'source', + u'span', + u'strong', + u'sub', + u'sup', + u'table', + u'tbody', + u'td', + u'tfoot', + u'th', + u'thead', + u'time', + u'title', + u'tr', + u'tt', + u'u', + u'ul', + u'var', + u'video', ], value_type=schema.TextLine(), missing_value=[], required=False) - class_blacklist = schema.List( - title=_(u'Filtered classes'), - description=_(u'These class names are not allowed in class ' - 'attributes.'), + custom_attributes = schema.List( + title=_(u'Custom attributes'), + description=_(u'These attributes are additionally allowed.'), default=[], - missing_value=[], value_type=schema.TextLine(), + missing_value=[], required=False) @@ -1271,8 +1308,8 @@ class ISiteSchema(Interface): default_page = schema.List( title=_(u'Default page IDs'), description=_( - u'Select which IDs (short names) can act as fallback default pages for', - u'a container.'), + u'Select which IDs (short names) can act as fallback ' + u'default pages for a container.'), required=True, default=[ u'index_html', diff --git a/Products/CMFPlone/patterns/settings.py b/Products/CMFPlone/patterns/settings.py index c568c07fc..5f3ae140f 100644 --- a/Products/CMFPlone/patterns/settings.py +++ b/Products/CMFPlone/patterns/settings.py @@ -3,7 +3,6 @@ from Acquisition import aq_parent from borg.localrole.interfaces import IFactoryTempFolder from plone.app.content.browser.interfaces import IFolderContentsView -from plone.app.layout.navigation.root import getNavigationRootObject from plone.app.widgets.utils import get_relateditems_options from plone.app.z3cform.utils import call_callables from plone.registry.interfaces import IRegistry @@ -81,7 +80,9 @@ def image_scales(self): 'plone.app.vocabularies.ImagesScales' ) vocabulary = factory(self.context) - ret = [{'title': translate(it.title), 'value': it.value} for it in vocabulary] + ret = [{ + 'title': translate(it.title), 'value': it.value} + for it in vocabulary] ret = sorted(ret, key=lambda it: it['title']) return json.dumps(ret) diff --git a/Products/CMFPlone/patterns/tinymce.py b/Products/CMFPlone/patterns/tinymce.py index 5e9e5dc14..c7bdab7ba 100644 --- a/Products/CMFPlone/patterns/tinymce.py +++ b/Products/CMFPlone/patterns/tinymce.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- +from lxml import html from plone.app.layout.navigation.root import getNavigationRootObject from plone.app.theming.utils import theming_policy from plone.registry.interfaces import IRegistry from Products.CMFCore.utils import getToolByName +from Products.CMFPlone.interfaces import IFilterSchema from Products.CMFPlone.interfaces import ITinyMCESchema from Products.CMFPlone.utils import get_portal from zope.component import getUtility @@ -20,6 +22,11 @@ def __init__(self, context, request): prefix="plone", check=False ) + self.filter_settings = getUtility(IRegistry).forInterface( + IFilterSchema, + prefix="plone", + check=False + ) self.nav_root = getNavigationRootObject( self.context, get_portal(), @@ -197,6 +204,25 @@ def get_tiny_config(self): except ValueError: pass + # add safe_html settings, which are useed in backend for filtering: + if not self.filter_settings.disable_filtering: + valid_tags = self.filter_settings.valid_tags + nasty_tags = self.filter_settings.nasty_tags + custom_attributes = self.filter_settings.custom_attributes + safe_attributes = [attr.decode() for attr in html.defs.safe_attrs] + valid_attributes = safe_attributes + custom_attributes + # valid_elements : 'a[href|target=_blank],strong/b,div[align],br' + tiny_valid_elements = [] + for tag in valid_tags: + tag_str = "%s[%s]" % (tag, "|".join(valid_attributes)) + tiny_valid_elements.append(tag_str) + # We want to remove the nasty tag including the content in the + # backend, so TinyMCE should allow them here. + for tag in nasty_tags: + tag_str = "%s[%s]" % (tag, "|".join(valid_attributes)) + tiny_valid_elements.append(tag_str) + tiny_config['valid_elements'] = ",".join(tiny_valid_elements) + if settings.other_settings: try: tiny_config.update(json.loads(settings.other_settings)) diff --git a/Products/CMFPlone/tests/robot/test_controlpanel_filter.robot b/Products/CMFPlone/tests/robot/test_controlpanel_filter.robot index 53695d65b..26399060f 100644 --- a/Products/CMFPlone/tests/robot/test_controlpanel_filter.robot +++ b/Products/CMFPlone/tests/robot/test_controlpanel_filter.robot @@ -14,6 +14,7 @@ Resource plone/app/robotframework/keywords.robot Resource plone/app/robotframework/saucelabs.robot Library Remote ${PLONE_URL}/RobotRemote +Library Collections Resource keywords.robot @@ -26,48 +27,26 @@ Test Teardown Run keywords Plone Test Teardown Scenario: Configure Filter Control Panel to filter out nasty tags Given a logged-in site administrator and the filter control panel - When I add 'h1' to the nasty tags list + When I add 'h1' to the nasty tags list and remove it from the valid tags list Then the 'h1' tag is filtered out when a document is saved Scenario: Configure Filter Control Panel to strip out tags Given a logged-in site administrator and the filter control panel - When I add 'h1' to the stripped tags list + When I remove 'h1' from the valid tags list Then the 'h1' tag is stripped when a document is saved Scenario: Configure Filter Control Panel to allow custom tags - Pass Execution This test currently fails because TinyMCE filters out the marquee tag and ignores the filter control panel settings. Given a logged-in site administrator and the filter control panel - When I add 'marquee' to the custom tags list - Then the 'marquee' tag is preserved when a document is saved + When I add 'foobar' to the valid tags list + Then the 'foobar' tag is preserved when a document is saved -Scenario: Configure Filter Control Panel to strip out attributes - Pass Execution Functionality is broken. Maybe even in Plone 4.3? +Scenario: Configure Filter Control Panel to allow custom attributes Given a logged-in site administrator and the filter control panel - When I add 'data-stripme' to the stripped attributes list - Then the 'data-stripme' attribute is stripped when a document is saved - -Scenario: Configure Filter Control Panel to strip out combinations - Pass Execution Functionality is broken. Maybe even in Plone 4.3? - Given a logged-in site administrator - and the filter control panel - When I add 'div h3' and 'data-foo' to the stripped out combinations - Then the 'data-foo' attribute of a 'div h3' combination is stripped when a document is saved - -Scenario: Configure Filter Control Panel to allow style attributes - Given a logged-in site administrator - and the filter control panel - When I add 'display' to the allowed style attributes - Then the 'display' style attribute is preserved when a document is saved - -Scenario: Configure Filter Control Panel to filter out classes - Pass Execution Functionality is broken. Maybe even in Plone 4.3? - Given a logged-in site administrator - and the filter control panel - When I add 'foobar' to the filtered classes - Then the 'foobar' class is filtered out when a document is saved + When I add 'foo-foo' to the custom attributes list + Then the 'foo-foo' attribute is preserved when a document is saved Scenario: Filter Control Panel displays information regarding caching when saved Given a logged-in site administrator @@ -93,39 +72,24 @@ Input RichText # --- WHEN ------------------------------------------------------------------- -I add '${tag}' to the nasty tags list +I add '${tag}' to the nasty tags list and remove it from the valid tags list Input Text name=form.widgets.nasty_tags ${tag} + Remove line from textarea form.widgets.valid_tags ${tag} Click Button Save Wait until page contains Changes saved -I add '${tag}' to the stripped tags list - Input Text name=form.widgets.stripped_tags ${tag} - Click Button Save - Wait until page contains Changes saved - -I add '${tag}' to the custom tags list - Input Text name=form.widgets.custom_tags ${tag} - Click Button Save - Wait until page contains Changes saved - -I add '${tag}' to the stripped attributes list - Input Text name=form.widgets.stripped_attributes ${tag} +I remove '${tag}' from the valid tags list + Remove line from textarea form.widgets.valid_tags ${tag} Click Button Save Wait until page contains Changes saved -I add '${tag}' to the filtered classes - Input Text name=form.widgets.class_blacklist ${tag} +I add '${tag}' to the valid tags list + Input Text name=form.widgets.valid_tags ${tag} Click Button Save Wait until page contains Changes saved -I add '${tag}' to the allowed style attributes - Input text name=form.widgets.style_whitelist ${tag} - Click Button Save - Wait until page contains Changes saved - -I add '${tags}' and '${attributes}' to the stripped out combinations - Input text name=form.stripped_combinations.1.tags ${tags} - Input text name=form.stripped_combinations.1.attributes ${attributes} +I add '${tag}' to the custom attributes list + Input Text name=form.widgets.custom_attributes ${tag} Click Button Save Wait until page contains Changes saved @@ -155,62 +119,23 @@ the 'h1' tag is stripped when a document is saved Page should contain heading XPath Should Match X Times //div[@id='content-core']//h1 0 message=h1 should have been stripped out -the 'marquee' tag is preserved when a document is saved - ${doc1_uid}= Create content id=doc1 title=Document 1 type=Document - Go To ${PLONE_URL}/doc1/edit - patterns are loaded - Input RichText <marquee>lorem ipsum</marquee> - Click Button Save - Wait until page contains Changes saved - XPath Should Match X Times //div[@id='content-core']//marquee 1 message=the marquee tag should have been preserved - -the 'data-stripme' attribute is stripped when a document is saved - ${doc1_uid}= Create content id=doc1 title=Document 1 type=Document - Go To ${PLONE_URL}/doc1/edit - patterns are loaded - Input RichText <h4 data-stripme="foo">lorem ipsum</h4> - Click Button Save - Wait until page contains Changes saved - Page should contain lorem ipsum - - XPath Should Match X Times //*[@id='content-core']//h4 1 message=h4 tag should be present - XPath Should Match X Times //*[@id='content-core']//h4[@data-stripme='foo'] 0 message=data-stripme attribute should have been filtered out - -the 'foobar' class is filtered out when a document is saved - ${doc1_uid}= Create content id=doc1 title=Document 1 type=Document - Go To ${PLONE_URL}/doc1/edit - patterns are loaded - Input RichText <h4 class="foobar">lorem ipsum</h4> - Click Button Save - Wait until page contains Changes saved - Page should contain lorem ipsum - - XPath Should Match X Times //*[@id='content-core']//h4 1 message=h4 tag should be present - XPath Should Match X Times //*[@id='content-core']//h4[@class='foobar'] 0 message=class foobar should have been filtered out - -the 'data-foo' attribute of a 'div h3' combination is stripped when a document is saved +the '${tag}' tag is preserved when a document is saved ${doc1_uid}= Create content id=doc1 title=Document 1 type=Document Go To ${PLONE_URL}/doc1/edit patterns are loaded - Input RichText <div><h3 data-foo="50">lorem ipsum</h3></div> + Input RichText <${tag}>lorem ipsum</${tag}> Click Button Save Wait until page contains Changes saved - Page should contain lorem ipsum - XPath Should Match X Times //*[@id='content-core']//div/h3 1 message=h4 tag should be present - XPath Should Match X Times //*[@id='content-core']//div/h3[@data-foo] 0 message=the data-foo attribute class should have been filtered out + XPath Should Match X Times //div[@id='content-core']//${tag} 1 message=the ${tag} tag should have been preserved - -the 'display' style attribute is preserved when a document is saved +the '${attribute}' attribute is preserved when a document is saved ${doc1_uid}= Create content id=doc1 title=Document 1 type=Document Go To ${PLONE_URL}/doc1/edit patterns are loaded - Input RichText <h4 style="display: block">lorem ipsum</h4> + Input RichText <span ${attribute}="foo">lorem ipsum</span> Click Button Save Wait until page contains Changes saved - Page should contain lorem ipsum - - XPath Should Match X Times //*[@id='content-core']//h4 1 message=h4 tag should be present - XPath Should Match X Times //*[@id='content-core']//h4[@style] 1 message=style attribute with display:block should be present + XPath Should Match X Times //span[@${attribute}] 1 message=the ${attribute} tag should have been preserved success message should contain information regarding caching Element Should Contain css=.portalMessage.warning HTML generation is heavily cached across Plone. You may have to edit existing content or restart your server to see the changes. diff --git a/Products/CMFPlone/tests/testPortalCreation.py b/Products/CMFPlone/tests/testPortalCreation.py index be2a5dcca..9139f60a8 100644 --- a/Products/CMFPlone/tests/testPortalCreation.py +++ b/Products/CMFPlone/tests/testPortalCreation.py @@ -19,6 +19,7 @@ from Products.CMFCore.utils import getToolByName from Products.CMFPlone import setuphandlers from Products.CMFPlone.factory import _DEFAULT_PROFILE +from Products.CMFPlone.interfaces import IFilterSchema from Products.CMFPlone.interfaces import INavigationSchema from Products.CMFPlone.interfaces import ISearchSchema from Products.CMFPlone.UnicodeSplitter import Splitter, I18NNormalizer @@ -186,7 +187,7 @@ def testNavTreeProperties(self): self.assertFalse( self.properties.navtree_properties.hasProperty('showAllParents')) self.assertFalse( - self.properties.navtree_properties.hasProperty('metaTypesNotToList')) + self.properties.navtree_properties.hasProperty('metaTypesNotToList')) # noqa self.assertFalse( self.properties.navtree_properties.hasProperty('sortAttribute')) self.assertFalse( @@ -558,7 +559,7 @@ def testObjectButtonActionsOnDefaultDocumentDoNotApplyToParent(self): urls = [a['url'] for a in buttons] for url in urls: self.assertFalse('index_html' not in url, - 'Action wrongly applied to parent object %s' % url) + 'Action wrongly applied to parent object %s' % url) # noqa def testObjectButtonActionsPerformCorrectAction(self): # only a manager would have proper permissions @@ -641,11 +642,14 @@ def testFolderHasAlbumView(self): self.assertTrue('atct_album_view' in self.types.Folder.view_methods) def testConfigurableSafeHtmlTransform(self): + registry = getUtility(IRegistry) + settings = registry.forInterface( + IFilterSchema, prefix="plone") # The safe_html transformation should be configurable try: - self.transforms.safe_html.get_parameter_value('disable_transform') + settings.disable_filtering except (AttributeError, KeyError): - self.fail('safe_html transformation not updated') + self.fail('Disabling of safe_html should be possible!') def testvcXMLRPCRemoved(self): # vcXMLRPC.js should no longer be registered diff --git a/Products/CMFPlone/tests/test_z3c_form_widgets.py b/Products/CMFPlone/tests/test_z3c_form_widgets.py index 2dd0e8669..32328fe3d 100644 --- a/Products/CMFPlone/tests/test_z3c_form_widgets.py +++ b/Products/CMFPlone/tests/test_z3c_form_widgets.py @@ -24,7 +24,7 @@ class FakeForm(object): class TestAttackVector(unittest.TestCase): layer = PRODUCTS_CMFPLONE_FUNCTIONAL_TESTING _widgets_to_test = WIDGETS_TO_TEST - _attack = '</textarea><script>alert("form.widgets.class_blacklist")</script>' # noqa + _attack = '</textarea><script>alert("form.widgets")</script>' # noqa def _terms(self): # For the SequenceWidget we need basic terms. diff --git a/setup.py b/setup.py index 2764017a2..1819071b8 100644 --- a/setup.py +++ b/setup.py @@ -37,6 +37,7 @@ 'lxml', 'mock', 'plone.app.robotframework>0.9.16', + 'robotframework-debuglibrary', 'plone.app.testing', 'zope.globalrequest', 'zope.testing', ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot