MultiRelationField.py 34.4 KB
Newer Older
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1 2
##############################################################################
#
3 4
# Copyright (c) 2002, 2004, 2006 Nexedi SARL and Contributors. 
#                                All Rights Reserved.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
5
#                    Jean-Paul Smets-Solanes <jp@nexedi.com>
6
#                    Romain Courteaud <romain@nexedi.com>
Jean-Paul Smets's avatar
Jean-Paul Smets committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################

from Products.Formulator import Widget, Validator
from Products.Formulator.Field import ZMIField
from Products.ERP5Type.Utils import convertToUpperCase
34
from Products.CMFCore.utils import getToolByName
35
from Products.PythonScripts.Utility import allow_class
36
from Products.ERP5Type.Message import Message
37
from AccessControl import ClassSecurityInfo
38
from types import StringType
39
from zLOG import LOG
40 41
from Products.Formulator.DummyField import fields
from Globals import get_request
42 43
from AccessControl import Unauthorized
from AccessControl import getSecurityManager
44 45 46 47 48 49 50

# Max. number of catalog result
MAX_SELECT = 30
NEW_CONTENT_PREFIX = '_newContent_'
# Key for sub listfield
SUB_FIELD_ID = 'relation'
ITEM_ID = 'item'
Romain Courteaud's avatar
Romain Courteaud committed
51
NO_VALUE = '??? (No Value)'
52

53
class MultiRelationStringFieldWidget(Widget.LinesTextAreaWidget,
54 55 56 57 58 59 60 61 62 63 64 65 66
                                     Widget.TextWidget, 
                                     Widget.ListWidget):
  """
  RelationStringField widget
  Works like a string field but includes one buttons
  - one search button which updates the field and sets a relation
  - creates object if not there
  """
  local_property_names = ['update_method', 'jump_method', 'allow_jump', 
                          'base_category', 'portal_type', 'allow_creation', 
                          'container_getter_id', 'catalog_index',
                          'relation_setter_id', 'columns', 'sort',
                          'parameter_list','list_method',
67 68
                          'first_item', 'items', 'size', 'extra_item',
                          ]
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197

  property_names = Widget.LinesTextAreaWidget.property_names + \
                   Widget.TextWidget.property_names + \
                   local_property_names
    
  # XXX Field to remove...
  update_method = fields.StringField('update_method',
                             title='Update Method',
                             description=(
      "The method to call to set the relation. Required."),
                             default="Base_validateRelation",
                             required=1)

  jump_method = fields.StringField('jump_method',
                             title='Jump Method',
                             description=(
      "The method to call to jump to the relation. Required."),
                             default="Base_jumpToRelatedDocument",
                             required=1)

  allow_jump = fields.CheckBoxField('allow_jump',
                             title='Allow Jump',
                             description=(
      "Do we allow to jump to the relation ?"),
                             default=1,
                             required=0)

  base_category = fields.StringField('base_category',
                             title='Base Category',
                             description=(
      "The method to call to set the relation. Required."),
                             default="",
                             required=1)

  portal_type = fields.ListTextAreaField('portal_type',
                             title='Portal Type',
                             description=(
      "The method to call to set the relation. Required."),
                             default="",
                             required=1)

  allow_creation = fields.CheckBoxField('allow_creation',
                             title='Allow Creation',
                             description=(
      "Do we allow to create new objects ?"),
                             default=1,
                             required=0)

  container_getter_id = fields.StringField('container_getter_id',
                             title='Container Getter Method',
                             description=(
      "The method to call to get a container object."),
                             default="",
                             required=0)

  catalog_index = fields.StringField('catalog_index',
                             title='Catalog Index',
                             description=(
      "The method to call to set the relation. Required."),
                             default="",
                             required=1)

  # XXX Is it a good idea to keep such a field ??
  # User can redefine setter method with a script (and so, don't use the API)
  relation_setter_id = fields.StringField('relation_setter_id',
                             title='Relation Update Method',
                             description=(
      "The method to invoke in order to update the relation"),
                             default="",
                             required=0)

  size = fields.IntegerField('size',
                             title='Size',
                             description=(
      "The display size in rows of the field. If set to 1, the "
      "widget will be displayed as a drop down box by many browsers, "
      "if set to something higher, a list will be shown. Required."),
                             default=1,
                             required=1)

  columns = fields.ListTextAreaField('columns',
                               title="Columns",
                               description=(
      "A list of attributes names to display."),
                               default=[],
                               required=0)

  sort = fields.ListTextAreaField('sort',
                               title='Default Sort',
                               description=('The default sort keys and order'),
                               default=[],
                               required=0)

  parameter_list = fields.ListTextAreaField('parameter_list',
                               title="Parameter List",
                               description=(
      "A list of paramters used for the portal_catalog."),
                               default=[],
                               required=0)

  list_method = fields.MethodField('list_method',
                               title='List Method',
                               description=('The method to use to list'
                                            'objects'),
                               default='',
                               required=0)

  # delete double in order to keep a usable ZMI...
  # XXX need to keep order !
  #property_names = dict([(i,0) for i in property_names]).keys() 
  _v_dict = {}
  _v_property_name_list = []
  for property_name in property_names:
    if not _v_dict.has_key(property_name):
      _v_property_name_list.append(property_name)
      _v_dict[property_name] = 1
  property_names = _v_property_name_list

  default_widget_rendering_instance = Widget.LinesTextAreaWidgetInstance

  def _generateRenderValueList(self, field, key, value_list, REQUEST):
    result_list = []
    need_validation = 0
    ####################################
    # Check value
    ####################################
    if isinstance(value_list, StringType):
      # Value is a string, reformat it correctly
      value_list = value_list.split("\n")
Romain Courteaud's avatar
Romain Courteaud committed
198 199 200 201 202 203
    else:
      # We get a list
      # rather than displaying nothing, display a marker when the
      # property is not set
      # XXX Translate ?
      value_list = [(x or NO_VALUE) for x in value_list]
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
    # Check all relation
    for i in range(len(value_list)):
      ###################################
      # Sub field
      ###################################
      relation_field_id = field.generate_subfield_key("%s_%s" % \
                                                      (SUB_FIELD_ID, i),
                                                      key=key)
      relation_item_id = field.generate_subfield_key("%s_%s" % \
                                                     (ITEM_ID, i),
                                                     key=key)
      relation_item_list = REQUEST.get(relation_item_id, None)
      value = value_list[i]
      if (relation_item_list is not None) and \
         (value != ''):
        need_validation = 1
      # If we get a empty string, display nothing !
      if value != '':
        result_list.append((Widget.TextWidgetInstance, relation_field_id, 
                            relation_item_list, value, i))
    if not need_validation:
      ###################################
      # Main field
      ###################################
      result_list = [(Widget.LinesTextAreaWidgetInstance, None, [], 
                      value_list, None)]
    return result_list

  def render(self, field, key, value, REQUEST):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
233
    """
234
    Render text input field.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
235
    """
236
    html_string = ''
237
    relation_field_index = REQUEST.get('_v_relation_field_index', 0)
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
    render_parameter_list = self._generateRenderValueList(
                                            field, key, value,
                                            REQUEST)
    ####################################
    # Render subfield
    ####################################
    html_string_list = []
    for widget_instance, relation_field_id, relation_item_list, \
                            value_instance, sub_index in render_parameter_list:
      sub_html_string = widget_instance.render(field, key, 
                                               value_instance, REQUEST)
      if relation_item_list is not None:
        if relation_item_list != []:
          ####################################
          # Render listfield
          ####################################

          REQUEST['relation_item_list'] = relation_item_list
          sub_html_string += '&nbsp;%s&nbsp;' % \
                                Widget.ListWidgetInstance.render(
                                field, relation_field_id, None, REQUEST)
          REQUEST['relation_item_list'] = None
260 261


262
        else:
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
          ####################################
          # Render wheel
          ####################################
          sub_html_string += self.render_wheel(
                    field, value_instance, REQUEST, 
                    relation_index=relation_field_index,
                    sub_index=sub_index)
      html_string_list.append(sub_html_string)  
    ####################################
    # Generate html
    ####################################
    html_string = '<br/>'.join(html_string_list)
    ####################################
    # Render jump
    ####################################
    if (value == field.get_value('default')):
      # XXX Default rendering with value...
      relation_html_string = self.render_relation_link(field, value, 
                                                       REQUEST)
      if relation_html_string != '':
        html_string += '&nbsp;&nbsp;%s' % relation_html_string
    ####################################
    # Update relation field index
    ####################################
    REQUEST.set('_v_relation_field_index', relation_field_index + 1) 
    return html_string

290
  def render_view(self, field, value, REQUEST=None):
291 292
    """
    Render read only field.
293 294

    XXX Improved rendering required
295 296
    """
    html_string = self.default_widget_rendering_instance.render_view(
297 298 299
                                                      field, value, REQUEST)
    if REQUEST is None:
      REQUEST = get_request()
300 301 302 303 304 305 306 307 308 309 310 311 312
    relation_html_string = self.render_relation_link(field, value, REQUEST)
    if relation_html_string != '':
      html_string += '&nbsp;&nbsp;%s' % relation_html_string
    return html_string

  def render_wheel(self, field, value, REQUEST, relation_index=0,
                   sub_index=None):
    """
    Render wheel used to display a listbox
    """
    here = REQUEST['here']
    portal_url = getToolByName(here, 'portal_url')
    portal_url_string = portal_url()
313
    portal_selections_url_string = here.portal_url.getRelativeContentURL(here.portal_selections)
314 315 316 317 318 319
    if sub_index is None:
      sub_index_string = ''
    else:
      sub_index_string = '_%s' % sub_index
    return '&nbsp;<input type="image" ' \
         'src="%s/images/exec16.png" value="update..." ' \
320
         'name="%s/viewSearchRelatedDocumentDialog%s%s' \
321
         ':method"/>' % \
322
           (portal_url_string, portal_selections_url_string,
323 324 325 326 327 328 329 330 331 332 333
           relation_index, sub_index_string)

  def render_relation_link(self, field, value, REQUEST):
    """
    Render link to the related object.
    """
    html_string = ''
    here = REQUEST['here']
    portal_url = getToolByName(here, 'portal_url')
    portal_url_string = portal_url()
    if (value not in ((), [], None, '')) and \
334
        field.get_value('allow_jump'):
335 336
      # Keep the selection name in the URL
      if REQUEST.get('selection_name') is not None:
337
        selection_name_html = '&amp;selection_name=%s&amp;selection_index=%s' % \
338 339 340 341
              (REQUEST.get('selection_name'), REQUEST.get('selection_index'))
      else:
        selection_name_html = ''
      # Generate plan link
342
      html_string += '<a href="%s/%s?field_id=%s&amp;form_id=%s%s">' \
343
                       '<img src="%s/images/jump.png" alt="jump" />' \
344 345 346 347 348 349 350
                     '</a>' % \
                (here.absolute_url(), 
                 field.get_value('jump_method'), 
                 field.id, field.aq_parent.id,
                 selection_name_html,
                 portal_url_string)
    return html_string
Jean-Paul Smets's avatar
Jean-Paul Smets committed
351

352 353 354 355
class MultiRelationEditor:
    """
      A class holding all values required to update a relation
    """
356 357 358 359
    def __init__(self, field_id, base_category, 
                 portal_type_list, 
                 portal_type_item, key, relation_setter_id, 
                 relation_editor_list):
360 361
      self.field_id = field_id
      self.base_category = base_category
362
      self.portal_type_list = portal_type_list
363 364 365 366 367 368 369 370 371
      self.portal_type_item = portal_type_item
      self.key = key
      self.relation_setter_id = relation_setter_id
      self.relation_editor_list = relation_editor_list

    def __call__(self, REQUEST):
      if self.relation_editor_list != None:
        value_list = []

372 373
        for value, uid, display_text, relation_key, item_key in \
                               self.relation_editor_list:
374
          value_list.append(value)
375 376 377
          if uid is not None:
            # Decorate the request so that we can display
            # the select item in a popup
378 379 380
            # XXX To be unified
            relation_field_id = relation_key
            relation_item_id = item_key
381 382 383 384 385
            REQUEST.set(relation_item_id, ((display_text, uid),))
        REQUEST.set(self.field_id, value_list) # XXX Dirty
      else:
        # Make sure no default value appears
        REQUEST.set(self.field_id, None) # XXX Dirty
386

387
    def view(self):
388 389 390
      return self.__dict__

    def edit(self, o):
391
      if self.relation_editor_list != None:
392

393
        relation_uid_list = []
394
        relation_object_list = []
395 396
        for value, uid, display_text, relation_key, item_key in \
                               self.relation_editor_list:
397
          if uid is not None:
398 399
            if isinstance(uid, StringType) and \
               uid.startswith(NEW_CONTENT_PREFIX):
400
              # Create a new content
401
              portal_type = uid[len(NEW_CONTENT_PREFIX):]
402 403 404
              portal_module = None
              for p_item in self.portal_type_item:
                if p_item[0] == portal_type:
405 406
                  portal_module = o.getPortalObject().getDefaultModuleId(
                                                            p_item[0])
407
              if portal_module is not None:
408 409
                portal_module_object = getattr(o.getPortalObject(), 
                                               portal_module)
410
                kw ={}
411
                kw[self.key] = value.replace('%', '')
412 413 414 415 416
                kw['portal_type'] = portal_type
                kw['immediate_reindex'] = 1
                new_object = portal_module_object.newContent(**kw)
                uid = new_object.getUid()
              else:
417
                raise
418 419 420
              
            relation_uid_list.append(int(uid))
            relation_object_list.append( o.portal_catalog.getObject(uid))
421

422
        # Edit relation
423 424
        if self.relation_setter_id:
          relation_setter = getattr(o, self.relation_setter_id)
425
          relation_setter((), portal_type=self.portal_type_list)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
426 427 428
          relation_setter(relation_uid_list,                  # relation setter is uid based
                          portal_type=self.portal_type_list)  # maybe not the best solution
                                                              # and inconsistent with bellow
429
        else:
430
          # we could call a generic method which create the setter method name
431 432 433
          if len(relation_object_list) == 1:
            set_method_name = '_set%sValue' % \
                         convertToUpperCase(self.base_category)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
434
            getattr(o, set_method_name)(relation_object_list[0],
435 436
                                        portal_type=self.portal_type_list,
                                        checked_permission='View')
437 438 439
          else:
            set_method_name = '_set%sValueList' % \
                         convertToUpperCase(self.base_category)
440
            getattr(o, set_method_name)(relation_object_list,
441 442
                                        portal_type=self.portal_type_list,
                                        checked_permission='View')
443 444 445

allow_class(MultiRelationEditor)

446 447 448 449 450 451 452
class MultiRelationStringFieldValidator(Validator.LinesValidator):
  """
      Validation includes lookup of relared instances
  """
  message_names = Validator.LinesValidator.message_names +\
                  ['relation_result_too_long', 'relation_result_ambiguous', 
                   'relation_result_empty',]
453

454 455 456
  relation_result_too_long = "Too many documents were found."
  relation_result_ambiguous = "Select appropriate document in the list."
  relation_result_empty = "No such document was found."
457

458 459 460
  # Relation field variable
  editor = MultiRelationEditor
  default_validator_instance = Validator.LinesValidatorInstance
461

462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
  def _generateItemUidList(self, field, key, relation_uid_list, REQUEST=None):
    """
    Generate tuple...
    """
    result_list = []
    for i in range(len(relation_uid_list)):
      # Generate a Item id for each value.
      relation_item_id = field.generate_subfield_key("%s_%s" % \
                                                     (ITEM_ID, i),
                                                     key=key)
      relation_uid = relation_uid_list[i]
      result_list.append((relation_item_id, relation_uid, None))
    return result_list

  def _generateFieldValueList(self, field, key, 
                              value_list, current_value_list):
    """
    Generate list of value, item_key
    """
    item_value_list = []
    if isinstance(current_value_list, StringType):
      current_value_list = [current_value_list]
    # Check value list
485 486
    if value_list != current_value_list: # Changes in the order or in the number of occurences
                                         # must be taken into account
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
      for i in range(len(value_list)):
        value = value_list[i]
        relation_field_id = field.generate_subfield_key("%s_%s" % \
                                                        (SUB_FIELD_ID, i),
                                                        key=key)
        relation_item_id = field.generate_subfield_key("%s_%s" % \
                                                       (ITEM_ID, i),
                                                       key=key)
        item_value_list.append((relation_field_id, value, relation_item_id))
      # Make possible to delete the content of the field.
      if item_value_list == []:
        relation_field_id = field.generate_subfield_key("%s" % \
                                                      SUB_FIELD_ID, key=key)
        relation_item_key = field.generate_subfield_key(ITEM_ID, key=key)
        item_value_list.append((relation_field_id, '', relation_item_key))
    return item_value_list

  def validate(self, field, key, REQUEST):
    """
    Validate the field.
    """
    raising_error_needed = 0
    relation_editor_list = None
    # Get some tool
    catalog_index = field.get_value('catalog_index')
    portal_type_list = [x[0] for x in field.get_value('portal_type')]
    portal_catalog = getToolByName(field, 'portal_catalog')
514

515 516 517 518 519 520
    ####################################
    # Check list input
    ####################################
    relation_field_id = field.generate_subfield_key("%s" % \
                                                    SUB_FIELD_ID, key=key)
    relation_uid_list = REQUEST.get(relation_field_id, None)
521

522 523 524 525
    ####################################
    # User clicked on the wheel
    ####################################
    need_to_revalidate = 1
526
    if relation_uid_list not in (None, ''):
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
      need_to_revalidate = 0
      relation_editor_list = []
      for relation_item_id, relation_uid, value in \
                  self._generateItemUidList(field, key, relation_uid_list,
                                            REQUEST=REQUEST):
        found = 0
        try:
          related_object = portal_catalog.getObject(relation_uid)
          display_text = str(related_object.getProperty(catalog_index))
          found = 1
        except ValueError:
          # Catch the error raised when the uid is a string
          if relation_uid.startswith(NEW_CONTENT_PREFIX):
            ##############################
            # New content was selected, but the 
            # form is not validated
            ##############################
            portal_type = relation_uid[len(NEW_CONTENT_PREFIX):]
            translated_portal_type = Message(domain='erp5_ui',
                                             message=portal_type)
547
            # XXX Replace New by Add
548 549 550 551 552 553
            message = Message(
                    domain='erp5_ui', message='New ${portal_type}',
                    mapping={'portal_type': translated_portal_type})
            display_text = message
          else:
            display_text = 'Object has been deleted'
554

555 556 557
        ################################
        # Modify if user modified his value
        ################################
558 559 560 561 562 563 564
        # XXX Does not work when user select a value in a ListField
#         if (found == 1) and \
#            (value != display_text):
#           relation_editor_list = None
#           need_to_revalidate = 1
#           REQUEST.set(relation_field_id, None)
#           break
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
        if value is None:
          value = display_text
        # Storing display_text as value is needed in this case
        relation_editor_list.append((value, 
                                     relation_uid, display_text,
                                     None, relation_item_id))
#                                      str(relation_uid), display_text,
    ####################################
    # User validate the form
    ####################################
    if need_to_revalidate == 1:
#     else:
      ####################################
      # Check the default field
      ####################################
      value_list = self.default_validator_instance.validate(field, 
                                                       key, REQUEST)
582 583
      # If the value is the same as the current field value, do nothing
      current_value_list = field.get_value('default')
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
      field_value_list = self._generateFieldValueList(field, key, value_list,
                                                    current_value_list)
      if len(field_value_list) != 0:
        ####################################
        # Values were changed
        ####################################
        relation_editor_list = []
        for relation_field_id, value, relation_item_id in field_value_list:
          if value == '':
            ####################################
            # User want to delete this line
            ####################################
            # Clean request if necessary
            if REQUEST.has_key(relation_field_id):
              for subdict_name in ['form', 'other']:
                subdict = getattr(REQUEST, subdict_name)
                if subdict.has_key(relation_field_id):
                  subdict.pop(relation_field_id)
            display_text = 'Delete the relation'
            relation_editor_list.append((value, None, 
                                     display_text, None, None))
            # XXX RelationField implementation
#         # We must be able to erase the relation
#         display_text = 'Delete the relation'
#         # Will be interpreted by Base_edit as "delete relation" 
#         # (with no uid and value = '')
#         relation_editor_list = [(value, None, 
#                                      display_text, None, None)]
          else:
            relation_uid = REQUEST.get(relation_field_id, None)
#             need_to_revalidate = 1
            if relation_uid not in (None, ''):
#               need_to_revalidate = 0
#               found = 0
              ####################################
              # User selected in a popup menu
              ####################################
              if isinstance(relation_uid, (list, tuple)):
                relation_uid = relation_uid[0]
              try:
                related_object = portal_catalog.getObject(relation_uid)
              except ValueError:
                # Catch the exception raised when the uid is a string
                related_object = None
Romain Courteaud's avatar
Romain Courteaud committed
628 629
              if related_object is not None:
                display_text = str(related_object.getProperty(catalog_index))
630
#                 found = 1
Romain Courteaud's avatar
Romain Courteaud committed
631
              else:
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667
                ##############################
                # New content was selected, but the 
                # form is not validated
                ##############################
                if relation_uid.startswith(NEW_CONTENT_PREFIX):
                  ##############################
                  # New content was selected, but the 
                  # form is not validated
                  ##############################
                  portal_type = relation_uid[len(NEW_CONTENT_PREFIX):]
                  translated_portal_type = Message(domain='erp5_ui',
                                                   message=portal_type)
                  message = Message(
                          domain='erp5_ui', message='New ${portal_type}',
                          mapping={'portal_type': translated_portal_type})
                  display_text = message
                else:
                  display_text = 'Object has been deleted'
#               ################################
#               # Modify if user modified his value
#               ################################
#               if (found == 1) and \
#                  (value != display_text):
#                 REQUEST.set(relation_field_id, None)
#                 need_to_revalidate = 1
#               else:
#                 # Check
#                 REQUEST.set(relation_item_id, ((display_text, relation_uid),))
#                 relation_editor_list.append((value, str(relation_uid), 
#                                             display_text, relation_field_id,
#                                             relation_item_id))
              REQUEST.set(relation_item_id, ((display_text, relation_uid),))
              relation_editor_list.append((value, str(relation_uid), 
                                          display_text, relation_field_id,
                                          relation_item_id))
#             if need_to_revalidate == 1:
668
            else:
669 670 671 672 673 674 675
              ####################################
              # User validate the form for this line
              ####################################
              kw ={}
              kw[catalog_index] = value
              kw['portal_type'] = portal_type_list
              kw['sort_on'] = catalog_index
676 677 678 679
              parameter_list = field.get_value('parameter_list')
              if len(parameter_list) > 0:
                for k,v in parameter_list:
                  kw[k] = v
680 681 682 683 684 685 686 687 688 689 690 691 692
              # Get the query results
              relation_list = portal_catalog(**kw)
              relation_uid_list = [x.uid for x in relation_list]
              menu_item_list = []
              if len(relation_list) >= MAX_SELECT:
                # If the length is long, raise an error
                # This parameter means we need listbox help
                # XXX XXX XXX Do we need to delete it ?
                REQUEST.set(relation_item_id, [])
                raising_error_needed = 1
                raising_error_value = 'relation_result_too_long'
              elif len(relation_list) == 1:
                # If the length is 1, return uid
693 694
                relation_uid = relation_uid_list[0]
                related_object = relation_list[0].getObject()
695 696
                if related_object is not None:
                  display_text = str(related_object.getProperty(catalog_index))
697 698 699
                  # Modify the value, in order to let the user 
                  # modify it later...
                  value = display_text
700
                else:
701
                  display_text = 'Object has been deleted'
702 703 704 705 706 707 708 709 710
                # XXX XXX XXX
                REQUEST.set(relation_item_id, ((display_text, 
                                                relation_uid),))
                relation_editor_list.append((value, relation_uid, 
                                             display_text, None,
                                             relation_item_id))
#                 relation_editor_list.append((0, value, relation_uid, 
#                                              display_text, None, None))
              elif len(relation_list) == 0:
711 712
                # Add blank line
                menu_item_list.append(('', ''))
713 714
                # If the length is 0, raise an error
                if field.get_value('allow_creation') == 1 :
715 716
                  user = getSecurityManager().getUser()
                  getDefaultModule = field.getDefaultModule
717 718
                  # XXX
                  for portal_type in portal_type_list:
719 720 721 722 723 724 725 726 727
                    try:
                      module = getDefaultModule(portal_type)
                    except ValueError:
                      pass
                    else:
                      if portal_type in module.getVisibleAllowedContentTypeList():
                        translated_portal_type = Message(domain='erp5_ui',
                                                         message=portal_type)
                        message = Message(
728
                                domain='erp5_ui', message='Add ${portal_type}',
729 730 731 732
                                mapping={'portal_type': translated_portal_type})
                        menu_item_list.append((message, 
                                               '%s%s' % (NEW_CONTENT_PREFIX, 
                                                         portal_type)))
733 734 735
                REQUEST.set(relation_item_id, menu_item_list)
                raising_error_needed = 1
                raising_error_value = 'relation_result_empty'
736
              else:
737 738 739 740 741
                # If the length is short, raise an error
                # len(relation_list) < MAX_SELECT:
                menu_item_list.extend([(
                                  x.getObject().getProperty(catalog_index),
                                  x.uid) for x in relation_list])
742 743
                # Add blank line
                menu_item_list.append(('', ''))
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764
                REQUEST.set(relation_item_id, menu_item_list)
                raising_error_needed = 1
                raising_error_value = 'relation_result_ambiguous'

    ##################################### 
    # Validate MultiRelation field
    ##################################### 
    if raising_error_needed:
      # Raise error
      self.raise_error(raising_error_value, field)
      return value_list
    else:
      # Can return editor
      base_category = field.get_value('base_category')
      portal_type_item = field.get_value('portal_type')
      relation_setter_id = field.get_value('relation_setter_id')
      return self.editor(field.id, 
                         base_category,
                         portal_type_list, 
                         portal_type_item, catalog_index, 
                         relation_setter_id, relation_editor_list)
765

Jean-Paul Smets's avatar
Jean-Paul Smets committed
766
MultiRelationStringFieldWidgetInstance = MultiRelationStringFieldWidget()
767
MultiRelationStringFieldValidatorInstance = MultiRelationStringFieldValidator()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
768 769

class MultiRelationStringField(ZMIField):
770 771
  meta_type = "MultiRelationStringField"
  security = ClassSecurityInfo()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
772

773 774
  widget = MultiRelationStringFieldWidgetInstance
  validator = MultiRelationStringFieldValidatorInstance
Jean-Paul Smets's avatar
Jean-Paul Smets committed
775

776 777
  security.declareProtected('Access contents information', 'get_orig_value')
  def get_orig_value(self, id):
778
    """
779
    Get value for id; don't do any override calculation.
780 781 782 783
    """
    if id in ('is_relation_field', 'is_multi_relation_field'):
      result = 1
    else:
784
      result = ZMIField.get_orig_value(self, id)
785
    return result
786 787 788 789 790 791 792 793 794 795 796 797

  security.declareProtected('Access contents information', 'get_value')
  def get_value(self, id, REQUEST=None, **kw):
    """Get value for id.

    Optionally pass keyword arguments that get passed to TALES
    expression.
    """
    if (id == 'items') and (REQUEST is not None):
      # relation_item_list is not editable for the RelationField
      result = REQUEST.get('relation_item_list', None)
    else:
798
      result = ZMIField.get_value(self, id, REQUEST=REQUEST, **kw)
799
    return result
800 801 802 803

# Register get_value
from Products.ERP5Form.ProxyField import registerOriginalGetValueClassAndArgument
registerOriginalGetValueClassAndArgument(MultiRelationStringField, 'items')