MultiRelationField.py 33.6 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 42 43 44 45 46 47 48
from Products.Formulator.DummyField import fields
from Globals import get_request

# 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
49
NO_VALUE = '??? (No Value)'
50

51
class MultiRelationStringFieldWidget(Widget.LinesTextAreaWidget,
52 53 54 55 56 57 58 59 60 61 62 63 64
                                     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',
65 66
                          'first_item', 'items', 'size', 'extra_item',
                          ]
67 68 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

  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
196 197 198 199 200 201
    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]
202 203 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
    # 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
231
    """
232
    Render text input field.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
233
    """
234
    html_string = ''
235
    relation_field_index = REQUEST.get('_v_relation_field_index', 0)
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
    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
          ####################################
          tales_expr = field.tales.get('items', None)
          defined_tales = 0
          if not tales_expr:
            defined_tales = 1
            from Products.Formulator.TALESField import TALESMethod
            # XXX XXX Do not write in the ZODB
            field.tales['items'] = TALESMethod('REQUEST/relation_item_list')

          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
          if defined_tales:
            # Delete default tales on the fly
            field.tales['items'] = None
268
        else:
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
          ####################################
          # 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.
    """
    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()
    portal_object = portal_url.getPortalObject()
    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..." ' \
         'name="%s/portal_selections/viewSearchRelatedDocumentDialog%s%s' \
         ':method"/>' % \
           (portal_url_string, portal_object.getPath(),
           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()
    portal_object = portal_url.getPortalObject()
    if (value not in ((), [], None, '')) and \
       (field.get_value('allow_jump') == 1):
      # Keep the selection name in the URL
      if REQUEST.get('selection_name') is not None:
341
        selection_name_html = '&amp;selection_name=%s&amp;selection_index=%s' % \
342 343 344 345
              (REQUEST.get('selection_name'), REQUEST.get('selection_index'))
      else:
        selection_name_html = ''
      # Generate plan link
346
      html_string += '<a href="%s/%s?field_id=%s&amp;form_id=%s%s">' \
347 348 349 350 351 352 353 354
                       '<img src="%s/images/jump.png" />' \
                     '</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
355

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

376 377
        for value, uid, display_text, relation_key, item_key in \
                               self.relation_editor_list:
378
          value_list.append(value)
379 380 381
          if uid is not None:
            # Decorate the request so that we can display
            # the select item in a popup
382 383 384
            # XXX To be unified
            relation_field_id = relation_key
            relation_item_id = item_key
385
            REQUEST.set(relation_item_id, ((display_text, uid),))
386
            # XXX Is it useful ?
387 388 389 390 391
            REQUEST.set(relation_field_id, 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
392

393
    def view(self):
394 395 396
      return self.__dict__

    def edit(self, o):
397
      if self.relation_editor_list != None:
398

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

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

allow_class(MultiRelationEditor)

450 451 452 453 454 455 456
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',]
457

458 459 460 461
  # XXX Do we need to translate here ?
  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."
462

463 464 465
  # Relation field variable
  editor = MultiRelationEditor
  default_validator_instance = Validator.LinesValidatorInstance
466

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

520 521 522 523 524 525
    ####################################
    # 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)
526

527 528 529 530
    ####################################
    # User clicked on the wheel
    ####################################
    need_to_revalidate = 1
531
    if relation_uid_list not in (None, ''):
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
      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)
552
            # XXX Replace New by Add
553 554 555 556 557 558
            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'
559

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

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

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

768 769
  widget = MultiRelationStringFieldWidgetInstance
  validator = MultiRelationStringFieldValidatorInstance
Jean-Paul Smets's avatar
Jean-Paul Smets committed
770

771 772
  security.declareProtected('Access contents information', 'get_orig_value')
  def get_orig_value(self, id):
773
    """
774
    Get value for id; don't do any override calculation.
775 776 777 778
    """
    if id in ('is_relation_field', 'is_multi_relation_field'):
      result = 1
    else:
779
      result = ZMIField.get_orig_value(self, id)
780
    return result