MultiRelationField.py 34.2 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 290 291 292
          ####################################
          # 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

  def render_view(self, field, value):
    """
    Render read only field.
293 294

    XXX Improved rendering required
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
    """
    html_string = self.default_widget_rendering_instance.render_view(
                                                      field, value)
    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
    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()
312
    portal_selections_url_string = here.portal_url.getRelativeContentURL(here.portal_selections)
313 314 315 316 317 318
    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..." ' \
319
         'name="%s/viewSearchRelatedDocumentDialog%s%s' \
320
         ':method"/>' % \
321
           (portal_url_string, portal_selections_url_string,
322 323 324 325 326 327 328 329 330 331 332
           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 \
333
        field.get_value('allow_jump'):
334 335
      # Keep the selection name in the URL
      if REQUEST.get('selection_name') is not None:
336
        selection_name_html = '&amp;selection_name=%s&amp;selection_index=%s' % \
337 338 339 340
              (REQUEST.get('selection_name'), REQUEST.get('selection_index'))
      else:
        selection_name_html = ''
      # Generate plan link
341
      html_string += '<a href="%s/%s?field_id=%s&amp;form_id=%s%s">' \
342
                       '<img src="%s/images/jump.png" alt="jump" />' \
343 344 345 346 347 348 349
                     '</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
350

351 352 353 354
class MultiRelationEditor:
    """
      A class holding all values required to update a relation
    """
355 356 357 358
    def __init__(self, field_id, base_category, 
                 portal_type_list, 
                 portal_type_item, key, relation_setter_id, 
                 relation_editor_list):
359 360
      self.field_id = field_id
      self.base_category = base_category
361
      self.portal_type_list = portal_type_list
362 363 364 365 366 367 368 369 370
      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 = []

371 372
        for value, uid, display_text, relation_key, item_key in \
                               self.relation_editor_list:
373
          value_list.append(value)
374 375 376
          if uid is not None:
            # Decorate the request so that we can display
            # the select item in a popup
377 378 379
            # XXX To be unified
            relation_field_id = relation_key
            relation_item_id = item_key
380 381 382 383 384
            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
385

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

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

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

421
        # Edit relation
422 423
        if self.relation_setter_id:
          relation_setter = getattr(o, self.relation_setter_id)
424
          relation_setter((), portal_type=self.portal_type_list)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
425 426 427
          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
428
        else:
429
          # we could call a generic method which create the setter method name
430 431 432
          if len(relation_object_list) == 1:
            set_method_name = '_set%sValue' % \
                         convertToUpperCase(self.base_category)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
433
            getattr(o, set_method_name)(relation_object_list[0],
434 435 436 437
                                        portal_type=self.portal_type_list)
          else:
            set_method_name = '_set%sValueList' % \
                         convertToUpperCase(self.base_category)
438
            getattr(o, set_method_name)(relation_object_list,
439
                                        portal_type=self.portal_type_list)
440 441 442

allow_class(MultiRelationEditor)

443 444 445 446 447 448 449
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',]
450

451 452 453
  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."
454

455 456 457
  # Relation field variable
  editor = MultiRelationEditor
  default_validator_instance = Validator.LinesValidatorInstance
458

459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
  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
482 483
    if value_list != current_value_list: # Changes in the order or in the number of occurences
                                         # must be taken into account
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
      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')
511

512 513 514 515 516 517
    ####################################
    # 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)
518

519 520 521 522
    ####################################
    # User clicked on the wheel
    ####################################
    need_to_revalidate = 1
523
    if relation_uid_list not in (None, ''):
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
      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)
544
            # XXX Replace New by Add
545 546 547 548 549 550
            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'
551

552 553 554
        ################################
        # Modify if user modified his value
        ################################
555 556 557 558 559 560 561
        # 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
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
        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)
579 580
      # If the value is the same as the current field value, do nothing
      current_value_list = field.get_value('default')
581 582 583 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
      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
625 626
              if related_object is not None:
                display_text = str(related_object.getProperty(catalog_index))
627
#                 found = 1
Romain Courteaud's avatar
Romain Courteaud committed
628
              else:
629 630 631 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
                ##############################
                # 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:
665
            else:
666 667 668 669 670 671 672
              ####################################
              # User validate the form for this line
              ####################################
              kw ={}
              kw[catalog_index] = value
              kw['portal_type'] = portal_type_list
              kw['sort_on'] = catalog_index
673 674 675 676
              parameter_list = field.get_value('parameter_list')
              if len(parameter_list) > 0:
                for k,v in parameter_list:
                  kw[k] = v
677 678 679 680 681 682 683 684 685 686 687 688 689
              # 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
690 691
                relation_uid = relation_uid_list[0]
                related_object = relation_list[0].getObject()
692 693
                if related_object is not None:
                  display_text = str(related_object.getProperty(catalog_index))
694 695 696
                  # Modify the value, in order to let the user 
                  # modify it later...
                  value = display_text
697
                else:
698
                  display_text = 'Object has been deleted'
699 700 701 702 703 704 705 706 707
                # 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:
708 709
                # Add blank line
                menu_item_list.append(('', ''))
710 711
                # If the length is 0, raise an error
                if field.get_value('allow_creation') == 1 :
712 713
                  user = getSecurityManager().getUser()
                  getDefaultModule = field.getDefaultModule
714 715
                  # XXX
                  for portal_type in portal_type_list:
716 717 718 719 720 721 722 723 724
                    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(
725
                                domain='erp5_ui', message='Add ${portal_type}',
726 727 728 729
                                mapping={'portal_type': translated_portal_type})
                        menu_item_list.append((message, 
                                               '%s%s' % (NEW_CONTENT_PREFIX, 
                                                         portal_type)))
730 731 732
                REQUEST.set(relation_item_id, menu_item_list)
                raising_error_needed = 1
                raising_error_value = 'relation_result_empty'
733
              else:
734 735 736 737 738
                # 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])
739 740
                # Add blank line
                menu_item_list.append(('', ''))
741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761
                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)
762

Jean-Paul Smets's avatar
Jean-Paul Smets committed
763
MultiRelationStringFieldWidgetInstance = MultiRelationStringFieldWidget()
764
MultiRelationStringFieldValidatorInstance = MultiRelationStringFieldValidator()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
765 766

class MultiRelationStringField(ZMIField):
767 768
  meta_type = "MultiRelationStringField"
  security = ClassSecurityInfo()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
769

770 771
  widget = MultiRelationStringFieldWidgetInstance
  validator = MultiRelationStringFieldValidatorInstance
Jean-Paul Smets's avatar
Jean-Paul Smets committed
772

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

  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:
795
      result = ZMIField.get_value(self, id, REQUEST=REQUEST, **kw)
796
    return result
797 798 799 800

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