From ba489a0fa442a7702a7f6c9f0ecf7abf15e5472a Mon Sep 17 00:00:00 2001 From: Julien Muchembled <jm@nexedi.com> Date: Wed, 21 May 2008 09:20:58 +0000 Subject: [PATCH] Fix {Field,Widget}.render_view API so that Listbox's non-editable cells can access 'cell' in TALES: * add a 'REQUEST=None' parameter to every render_view method: * new patches in FormulatorPatch to fix render_view of Field, Widget, MultiItemsWidget, LabelWidget, FileWidget, PasswordWidget and RadioWidget * reorder parameters in OOoChartWidget.render_view * add a 'REQUEST=None' parameter to DurationField.render_sub_field_view * forward REQUEST to field.get_value in: * ListWidget_render_view (my goal) * TALESWidget_render_view (why not?) * PatchedLinkWidget.render_view and MultiRelationStringFieldWidget.render_view needn't call get_request anymore if REQUEST isn't None * PatchedLinkWidget.render_view: change REQUEST.get('cell') into getattr(REQUEST,'cell',None) since 'cell' may be an attribute of REQUEST * add a unit test to check the signature of all registered fields/widgets git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@21048 20353a03-c40f-0410-a6d1-a30d3c3de9de --- product/ERP5Form/DurationField.py | 12 ++--- product/ERP5Form/EditorField.py | 2 +- product/ERP5Form/FormulatorPatch.py | 63 ++++++++++++++++++-------- product/ERP5Form/ImageField.py | 4 +- product/ERP5Form/MultiRelationField.py | 7 +-- product/ERP5Form/OOoChart.py | 2 +- product/ERP5Form/POSBox.py | 2 +- product/ERP5Form/ParallelListField.py | 2 +- product/ERP5Form/tests/testFields.py | 19 ++++++++ 9 files changed, 80 insertions(+), 33 deletions(-) diff --git a/product/ERP5Form/DurationField.py b/product/ERP5Form/DurationField.py index 8c9801ed80..935c94d97a 100644 --- a/product/ERP5Form/DurationField.py +++ b/product/ERP5Form/DurationField.py @@ -64,7 +64,7 @@ class DurationWidget(FormulatorPatch.IntegerWidget): default="", required=1) - def render_view(self, field, value): + def render_view(self, field, value, REQUEST=None): sub_field_render_list = [] for title, sub_key, convertion in (('Hour', 'hour', HOUR_IN_SECOND), ('Minute', 'minute', MINUTE_IN_SECOND)): @@ -74,10 +74,10 @@ class DurationWidget(FormulatorPatch.IntegerWidget): sub_value, value = divmod(value, convertion) sub_field_render_list.append(self.render_sub_field_view( - field,sub_value)) + field,sub_value, REQUEST)) # Render second sub_field_render_list.append(self.render_sub_field_view( - field, value)) + field, value, REQUEST)) return ':'.join(sub_field_render_list) def render(self, field, key, value, REQUEST): @@ -97,12 +97,12 @@ class DurationWidget(FormulatorPatch.IntegerWidget): value, REQUEST, 'second')) return ':'.join(sub_field_render_list) - def render_sub_field_view(self, field, value): + def render_sub_field_view(self, field, value, REQUEST=None): """ Render dynamically a subfield """ - return FormulatorPatch.IntegerFieldWidgetInstance.render_view(field, - value) + return FormulatorPatch.IntegerFieldWidgetInstance.render_view(field, value, + REQUEST) def render_sub_field(self, field, key, value, REQUEST, keyword): """ diff --git a/product/ERP5Form/EditorField.py b/product/ERP5Form/EditorField.py index 1d045a1bf6..5706653ae3 100644 --- a/product/ERP5Form/EditorField.py +++ b/product/ERP5Form/EditorField.py @@ -87,7 +87,7 @@ class EditorWidget(Widget.TextAreaWidget): 'inputname' : key }) - def render_view(self, field, value): + def render_view(self, field, value, REQUEST=None): """ Render form in view only mode. """ diff --git a/product/ERP5Form/FormulatorPatch.py b/product/ERP5Form/FormulatorPatch.py index c0e6da0e79..7c4f3f00f4 100644 --- a/product/ERP5Form/FormulatorPatch.py +++ b/product/ERP5Form/FormulatorPatch.py @@ -68,6 +68,11 @@ def Field_render(self, value=None, REQUEST=None, key=None): """ return self._render_helper(self.generate_field_key(key=key), value, REQUEST) +def Field_render_view(self, value=None, REQUEST=None): + """Render value to be viewed. + """ + return self.widget.render_view(self, value, REQUEST) + def Field_render_sub_field(self, id, value=None, REQUEST=None, key=None): """Render a sub field, as part of complete rendering of widget in a form. Works like render() but for sub field. @@ -98,7 +103,7 @@ def Field_render_helper(self, key, value, REQUEST): if self.get_value('hidden', REQUEST=REQUEST): return self.widget.render_hidden(self, key, value, REQUEST) elif (not self.get_value('editable', REQUEST=REQUEST)): - return self.widget.render_view(self, value) + return self.widget.render_view(self, value, REQUEST) else: return self.widget.render(self, key, value, REQUEST) @@ -113,6 +118,7 @@ def Field_render_odf(self, field=None, key=None, value=None, REQUEST=None, rende Field.generate_field_key = Field_generate_field_key Field.render = Field_render +Field.render_view = Field_render_view Field.render_sub_field = Field_render_sub_field Field.generate_subfield_key = Field_generate_subfield_key Field.validate_sub_field = Field_validate_sub_field @@ -307,7 +313,7 @@ def CheckBoxWidget_render(self, field, key, value, REQUEST): CheckBoxWidget.render = CheckBoxWidget_render -def CheckBoxWidget_render_view(self, field, value): +def CheckBoxWidget_render_view(self, field, value, REQUEST=None): """Render checkbox in view mode. """ if value: @@ -332,26 +338,28 @@ from Products.Formulator.StandardFields import LinkField from Globals import get_request from urlparse import urljoin -class PatchedLinkWidget(TextWidget) : - def render_view(self, field, value) : +class PatchedLinkWidget(TextWidget): + def render_view(self, field, value, REQUEST=None): """Render link. """ - REQUEST = get_request() - link_type = field.get_value('link_type') + link_type = field.get_value('link_type', REQUEST=REQUEST) + if REQUEST is None: + REQUEST = get_request() if link_type == 'internal': value = urljoin(REQUEST['BASE0'], value) elif link_type == 'relative': value = urljoin(REQUEST['URL1'], value) - return '<a href="%s">%s</a>' % (value, field.get_value('title', cell=REQUEST.get('cell'))) + return '<a href="%s">%s</a>' % (value, + field.get_value('title', cell=getattr(REQUEST,'cell',None))) PatchedLinkWidgetInstance = PatchedLinkWidget() LinkField.widget = PatchedLinkWidgetInstance # Patch the render_view of TextField to enclose the value within <span> html tags if css class defined -def TextWidget_patched_render_view(self, field, value): +def TextWidget_patched_render_view(self, field, value, REQUEST=None): """Render text as non-editable. This renderer is designed to be type error resistant. in we get a non string value. It does escape the result @@ -405,11 +413,11 @@ class IntegerWidget(TextWidget) : size=field.get_value('display_width'), extra=field.get_value('extra')) - def render_view(self, field, value): + def render_view(self, field, value, REQUEST=None): """Render a non-editable interger.""" if isinstance(value, float): value = int(value) - return TextWidget.render_view(self, field, value) + return TextWidget.render_view(self, field, value, REQUEST) from Products.Formulator.StandardFields import IntegerField @@ -487,9 +495,17 @@ def Widget_render_hidden(self, field, key, value, REQUEST): extra=extra) return result +def Widget_render_view(self, field, value, REQUEST=None): + """Renders this widget for public viewing. + """ + # default implementation + if value is None: + return '' + return value + Widget.render_hidden = Widget_render_hidden # default render_pdf for a Widget -Widget.render_pdf = Widget.render_view +Widget.render_pdf = Widget.render_view = Widget_render_view def Widget_render_css(self, field, REQUEST): """ @@ -518,6 +534,17 @@ def Widget_get_javascript_list(self, field, REQUEST): return [] Widget.get_javascript_list = Widget_get_javascript_list + +from Products.Formulator import Widget as WidgetModule + +for widget_name in ('MultiItemsWidget', 'LabelWidget', + 'FileWidget', 'PasswordWidget', 'RadioWidget'): + widget = getattr(WidgetModule, widget_name) + widget._old_render_view = widget.render_view + widget.render_view = lambda self, field, value, REQUEST=None: \ + self._old_render_view(field, value) + + from Products.Formulator.Validator import LinesValidator def LinesValidator_validate(self, field, key, REQUEST): @@ -747,14 +774,14 @@ def ListWidget_render(self, field, key, value, REQUEST): return "\n".join([list_widget, input_hidden]) -def ListWidget_render_view(self, field, value): +def ListWidget_render_view(self, field, value, REQUEST=None): """ This method is not as efficient as using a StringField in read only. Always consider to change the field in your Form. """ if value is None: return '' - title_list = [x[0] for x in field.get_value("items") if x[1]==value] + title_list = [x[0] for x in field.get_value("items", REQUEST=REQUEST) if x[1]==value] if len(title_list) == 0: return "??? (%s)" % value else: @@ -986,7 +1013,7 @@ class PatchedDateTimeWidget(DateTimeWidget): else: return date_result - def render_view(self, field, value): + def render_view(self, field, value, REQUEST=None): return self.format_value(field, value, mode='html') def render_pdf(self, field, value): @@ -1324,7 +1351,7 @@ class FloatWidget(TextWidget): **extra_keys) - def render_view(self, field, value): + def render_view(self, field, value, REQUEST=None): """ Render Float display field. This patch add: @@ -1497,12 +1524,12 @@ Field.get_javascript_list = Field_get_javascript_list from Products.Formulator.TALESField import TALESWidget -def TALESWidget_render_view(self, field, value): +def TALESWidget_render_view(self, field, value, REQUEST=None): """ Render TALES as read only """ if value == None: - text = field.get_value('default') + text = field.get_value('default', REQUEST=REQUEST) else: if value != "": text = value._text @@ -1559,7 +1586,7 @@ def LinesTextAreaWidget_render(self, field, key, value, REQUEST): LinesTextAreaWidget.render = LinesTextAreaWidget_render original_LinesTextAreaWidget_render_view = LinesTextAreaWidget.render_view -def LinesTextAreaWidget_render_view(self, field, value): +def LinesTextAreaWidget_render_view(self, field, value, REQUEST=None): if isinstance(value, (str, unicode)): value = [value] return original_LinesTextAreaWidget_render_view(self, field, value) diff --git a/product/ERP5Form/ImageField.py b/product/ERP5Form/ImageField.py index 10c14c4161..25fb5b6ca9 100644 --- a/product/ERP5Form/ImageField.py +++ b/product/ERP5Form/ImageField.py @@ -69,9 +69,9 @@ class ImageFieldWidget(Widget.TextWidget): def render(self, field, key, value, REQUEST): """Render image field as a link to the image """ - return self.render_view(field, value) + return self.render_view(field, value, REQUEST) - def render_view(self, field, value): + def render_view(self, field, value, REQUEST=None): """Render image field as a link to the image """ # Url is already defined in value diff --git a/product/ERP5Form/MultiRelationField.py b/product/ERP5Form/MultiRelationField.py index 1959410b06..7e765d95ce 100644 --- a/product/ERP5Form/MultiRelationField.py +++ b/product/ERP5Form/MultiRelationField.py @@ -287,15 +287,16 @@ class MultiRelationStringFieldWidget(Widget.LinesTextAreaWidget, REQUEST.set('_v_relation_field_index', relation_field_index + 1) return html_string - def render_view(self, field, value): + def render_view(self, field, value, REQUEST=None): """ Render read only field. XXX Improved rendering required """ html_string = self.default_widget_rendering_instance.render_view( - field, value) - REQUEST = get_request() + field, value, REQUEST) + if REQUEST is None: + REQUEST = get_request() relation_html_string = self.render_relation_link(field, value, REQUEST) if relation_html_string != '': html_string += ' %s' % relation_html_string diff --git a/product/ERP5Form/OOoChart.py b/product/ERP5Form/OOoChart.py index f1d18c2e03..90975e6e03 100644 --- a/product/ERP5Form/OOoChart.py +++ b/product/ERP5Form/OOoChart.py @@ -378,7 +378,7 @@ class OOoChartWidget(Widget.Widget): return extra_argument_dict - def render_view(self, field, value, key=None, REQUEST=None, render_format='html'): + def render_view(self, field, value, REQUEST=None, key=None, render_format='html'): """ Render a Chart in read-only. """ diff --git a/product/ERP5Form/POSBox.py b/product/ERP5Form/POSBox.py index 7ba92fc349..cb8233f341 100644 --- a/product/ERP5Form/POSBox.py +++ b/product/ERP5Form/POSBox.py @@ -259,7 +259,7 @@ class POSBoxWidget(Widget.Widget): resource_category_fastResourceEntry = field.get_value('resource_category_fastResourceEntry') ) - def render_view(self, field, value): + def render_view(self, field, value, REQUEST=None): """ Render point of sales widget """ diff --git a/product/ERP5Form/ParallelListField.py b/product/ERP5Form/ParallelListField.py index 5cc807790c..7fa709af90 100644 --- a/product/ERP5Form/ParallelListField.py +++ b/product/ERP5Form/ParallelListField.py @@ -145,7 +145,7 @@ class ParallelListWidget(Widget.MultiListWidget, result = self.sub_widget[sub_field_property_dict['field_type']].render_view( field, sub_field_property_dict['value'], - ) + REQUEST) for parameter in ('title', 'required', 'size', 'default', 'first_item', 'items'): # As it doesn't seem possible to delete value in the REQUEST, diff --git a/product/ERP5Form/tests/testFields.py b/product/ERP5Form/tests/testFields.py index 4eab2e2410..9f997cf4c9 100644 --- a/product/ERP5Form/tests/testFields.py +++ b/product/ERP5Form/tests/testFields.py @@ -51,6 +51,7 @@ from Testing import ZopeTestCase ZopeTestCase.installProduct('ERP5Form') from Acquisition import aq_base +from Products.Formulator.FieldRegistry import FieldRegistry from Products.Formulator.StandardFields import FloatField from Products.Formulator.StandardFields import StringField from Products.Formulator.StandardFields import DateTimeField @@ -65,6 +66,23 @@ from Products.ERP5Form import Form from Products.ERP5Form import ProxyField +class TestRenderViewAPI(unittest.TestCase): + """For all fields and widgets, tests the signature of the render_view method. + In particular, render_view must accept a 'REQUEST' parameter after 'value'. + """ + + def getTitle(self): + return "{Field,Widget}.render_view" + + def test_signature(self): + for field in FieldRegistry.get_field_classes().itervalues(): + self.assertEquals(('self', 'value', 'REQUEST'), + field.render_view.im_func.func_code.co_varnames) + if field is not ProxyField.ProxyField: + self.assertEquals(('self', 'field', 'value', 'REQUEST'), + field.widget.render_view.im_func.func_code.co_varnames[:4]) + + class TestFloatField(unittest.TestCase): """Tests Float field """ @@ -515,6 +533,7 @@ def makeDummyOid(): def test_suite(): suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestRenderViewAPI)) suite.addTest(unittest.makeSuite(TestFloatField)) suite.addTest(unittest.makeSuite(TestStringField)) suite.addTest(unittest.makeSuite(TestProxyField)) -- 2.30.9