Back to index

plone3  3.1.7
filter.py
Go to the documentation of this file.
00001 from plone.fieldsets.fieldsets import FormFieldsets
00002 
00003 from zope.interface import Interface
00004 from zope.component import adapts
00005 from zope.interface import implements
00006 from zope import schema
00007 from zope.app.form import CustomWidgetFactory
00008 from zope.app.form.browser import ObjectWidget
00009 from zope.app.form.browser import ListSequenceWidget
00010 
00011 from Products.CMFCore.utils import getToolByName
00012 from Products.CMFDefault.formlib.schema import SchemaAdapterBase
00013 from Products.CMFPlone import PloneMessageFactory as _
00014 from Products.CMFPlone.interfaces import IPloneSiteRoot
00015 from Products.PortalTransforms.transforms.safe_html import VALID_TAGS
00016 
00017 from form import ControlPanelForm
00018 
00019 XHTML_TAGS = set(
00020     'a abbr acronym address area b base bdo big blockquote body br '
00021     'button caption cite code col colgroup dd del div dfn dl dt em '
00022     'fieldset form h1 h2 h3 h4 h5 h6 head hr html i img input ins kbd '
00023     'label legend li link map meta noscript object ol optgroup option '
00024     'p param pre q samp script select small span strong style sub sup '
00025     'table tbody td textarea tfoot th thead title tr tt ul var'.split())
00026 
00027 
00028 class ITagAttrPair(Interface):
00029     tags = schema.TextLine(title=u"tags")
00030     attributes = schema.TextLine(title=u"attributes")
00031 
00032 class TagAttrPair:
00033     implements(ITagAttrPair)
00034     def __init__(self, tags='', attributes=''):
00035         self.tags = tags
00036         self.attributes = attributes
00037 
00038 class IFilterTagsSchema(Interface):
00039 
00040     nasty_tags = schema.List(
00041         title=_(u'Nasty tags'),
00042         description=_(u"These tags, and their content are completely blocked "
00043                       "when a page is saved or rendered."),
00044         default=[u'applet', u'embed', u'object', u'script'],
00045         value_type=schema.TextLine(),
00046         required=False)
00047 
00048     stripped_tags = schema.List(
00049         title=_(u'Stripped tags'),
00050         description=_(u"These tags are stripped when saving or rendering, "
00051                       "but any content is preserved."),
00052         default=[u'font', ],
00053         value_type=schema.TextLine(),
00054         required=False)
00055 
00056     custom_tags = schema.List(
00057         title=_(u'Custom tags'),
00058         description=_(u"Add tag names here for tags which are not part of "
00059                       "XHTML but which should be permitted."),
00060         default=[],
00061         value_type=schema.TextLine(),
00062         required=False)
00063 
00064 
00065 class IFilterAttributesSchema(Interface):
00066     stripped_attributes = schema.List(
00067         title=_(u'Stripped attributes'),
00068         description=_(u"These attributes are stripped from any tag when "
00069                       "saving."),
00070         default=(u'dir lang valign halign border frame rules cellspacing '
00071                  'cellpadding bgcolor').split(),
00072         value_type=schema.TextLine(),
00073         required=False)
00074 
00075     stripped_combinations = schema.List(
00076         title=_(u'Stripped combinations'),
00077         description=_(u"These attributes are stripped from any tag when "
00078                       "saving."),
00079         default=[],
00080         #default=u'dir lang valign halign border frame rules cellspacing cellpadding bgcolor'.split()
00081         value_type=schema.Object(ITagAttrPair, title=u"combination"),
00082         required=False)
00083 
00084 class IFilterEditorSchema(Interface):
00085     style_whitelist = schema.List(
00086         title=_(u'Permitted styles'),
00087         description=_(u'These CSS styles are allowed in style attributes.'),
00088         default=u'text-align list-style-type float'.split(),
00089         value_type=schema.TextLine(),
00090         required=False)
00091 
00092     class_blacklist = schema.List(
00093         title=_(u'Filtered classes'),
00094         description=_(u'These class names are not allowed in class '
00095                       'attributes.'),
00096         default=[],
00097         value_type=schema.TextLine(),
00098         required=False)
00099 
00100 
00101 class IFilterSchema(IFilterTagsSchema, IFilterAttributesSchema,
00102                     IFilterEditorSchema):
00103     """Combined schema for the adapter lookup.
00104     """
00105 
00106 class FilterControlPanelAdapter(SchemaAdapterBase):
00107     adapts(IPloneSiteRoot)
00108     implements(IFilterSchema)
00109 
00110     def __init__(self, context):
00111         super(FilterControlPanelAdapter, self).__init__(context)
00112         self.context = context
00113         self.transform = getattr(
00114             getToolByName(context, 'portal_transforms'), 'safe_html')
00115         self.kupu_tool = getToolByName(context, 'kupu_library_tool')
00116 
00117     def _settransform(self, **kwargs):
00118         # Cannot pass a dict to set transform parameters, it has
00119         # to be separate keys and values
00120         # Also the transform requires all dictionary values to be set
00121         # at the same time: other values may be present but are not
00122         # required.
00123         for k in ('valid_tags', 'nasty_tags'):
00124             if k not in kwargs:
00125                 kwargs[k] = self.transform.get_parameter_value(k)
00126 
00127         for k in list(kwargs):
00128             if isinstance(kwargs[k], dict):
00129                 v = kwargs[k]
00130                 kwargs[k+'_key'] = v.keys()
00131                 kwargs[k+'_value'] = [str(s) for s in v.values()]
00132                 del kwargs[k]
00133         self.transform.set_parameters(**kwargs)
00134         self.transform._p_changed = True
00135         self.transform.reload()
00136 
00137     @apply
00138     def nasty_tags():
00139         def get(self):
00140             return sorted(self.transform.get_parameter_value('nasty_tags'))
00141         def set(self, value):
00142             value = dict.fromkeys(value, 1)
00143             valid = self.transform.get_parameter_value('valid_tags')
00144             for v in value:
00145                 if v in valid:
00146                     del valid[v]
00147             self._settransform(nasty_tags=value, valid_tags=valid)
00148         return property(get, set)
00149 
00150     @apply
00151     def stripped_tags():
00152         def get(self):
00153             valid = set(self.transform.get_parameter_value('valid_tags'))
00154             stripped = XHTML_TAGS - valid
00155             return sorted(stripped)
00156         def set_(self, value):
00157             valid = dict(self.transform.get_parameter_value('valid_tags'))
00158             stripped = set(value)
00159             for v in XHTML_TAGS:
00160                 if v in stripped:
00161                     if v in valid:
00162                         del valid[v]
00163                 else:
00164                     valid[v] = VALID_TAGS.get(v, 1)
00165 
00166             # Nasty tags must never be valid
00167             for v in self.nasty_tags:
00168                 if v in valid:
00169                     del valid[v]
00170             self._settransform(valid_tags=valid)
00171             self.kupu_tool.set_stripped_tags(value)
00172 
00173         return property(get, set_)
00174 
00175     @apply
00176     def custom_tags():
00177         def get(self):
00178             valid = set(self.transform.get_parameter_value('valid_tags'))
00179             custom = valid - XHTML_TAGS
00180             return sorted(custom)
00181         def set_(self, value):
00182             valid = dict(self.transform.get_parameter_value('valid_tags'))
00183             # Remove all non-standard tags
00184             for v in valid.keys():
00185                 if v not in XHTML_TAGS:
00186                     del valid[v]
00187             # Now add in the custom tags
00188             for v in value:
00189                 if v not in valid:
00190                     valid[v] = 1
00191 
00192             self._settransform(valid_tags=valid)
00193 
00194         return property(get, set_)
00195 
00196 
00197     @apply
00198     def style_whitelist():
00199         def get(self):
00200             return self.kupu_tool.getStyleWhitelist()
00201         def set(self, value):
00202             self.kupu_tool.style_whitelist = list(value)
00203         return property(get, set)
00204 
00205     @apply
00206     def class_blacklist():
00207         '''Ideally the form should allow setting a class whitelist,
00208         but that will have to be added later.'''
00209         def get(self):
00210             return  self.kupu_tool.getClassBlacklist()
00211         def set(self, value):
00212             self.kupu_tool.class_blacklist = list(value)
00213         return property(get, set)
00214 
00215     @apply
00216     def stripped_attributes():
00217         def get(self):
00218             return self.kupu_tool.get_stripped_attributes()
00219         def set(self, value):
00220             self.kupu_tool.set_stripped_attributes(value)
00221         return property(get, set)
00222 
00223     @apply
00224     def stripped_combinations():
00225         def get(self):
00226             return  [TagAttrPair(' '.join(t),' '.join(a)) for (t,a) in \
00227                      self.kupu_tool.get_stripped_combinations()]
00228         def set(self, value):
00229             stripped = []
00230             for ta in value:
00231                 tags = ta.tags.replace(',', ' ').split()
00232                 attributes = ta.attributes.replace(',', ' ').split()
00233                 stripped.append((tags,attributes))
00234 
00235             self.kupu_tool.set_stripped_combinations(stripped)
00236         return property(get, set)
00237 
00238 
00239 filtertagset = FormFieldsets(IFilterTagsSchema)
00240 filtertagset.id = 'filtertags'
00241 filtertagset.label = _(u'label_filtertags', default=u'Tags')
00242 
00243 filterattributes = FormFieldsets(IFilterAttributesSchema)
00244 filterattributes.id = 'filterattributes'
00245 filterattributes.label = _(u'label_filterattributes', default=u'Attributes')
00246 
00247 filtereditor = FormFieldsets(IFilterEditorSchema)
00248 filtereditor.id = 'filtereditor'
00249 filtereditor.label = _(u'filterstyles', default=u'Styles')
00250 
00251 tagattr_widget = CustomWidgetFactory(ObjectWidget, TagAttrPair)
00252 combination_widget = CustomWidgetFactory(ListSequenceWidget,
00253                                          subwidget=tagattr_widget)
00254 
00255 class FilterControlPanel(ControlPanelForm):
00256 
00257     form_fields = FormFieldsets(filtertagset, filterattributes, filtereditor)
00258     form_fields['stripped_combinations'].custom_widget = combination_widget
00259 
00260     label = _("HTML Filter settings")
00261     description = _("Plone filters HTML tags that are considered security "
00262                     "risks. Be aware of the implications before making "
00263                     "changes below. By default only tags defined in XHTML "
00264                     "are permitted. In particular, to allow 'embed' as a tag "
00265                     "you must both remove it from 'Nasty tags' and add it to "
00266                     "'Custom tags'. Although the form will update "
00267                     "immediately to show any changes you make, your changes "
00268                     "are not saved until you press the 'Save' button.")
00269     form_name = _("HTML Filter settings")
00270