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 += '&nbsp;&nbsp;%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