ListBox.py 108 KB
Newer Older
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1 2 3
##############################################################################
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4
#                    Jean-Paul Smets-Solanes <jp@nexedi.com>
Jean-Paul Smets's avatar
Jean-Paul Smets committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
#
# 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.
#
##############################################################################

29
import string, types, sys
30
from OFS.Traversable import NotFound
31
from AccessControl import ClassSecurityInfo
Jean-Paul Smets's avatar
Jean-Paul Smets committed
32 33 34 35
from Products.Formulator.DummyField import fields
from Products.Formulator import Widget, Validator
from Products.Formulator.Field import ZMIField
from Products.Formulator.Form import BasicForm
36
from Products.Formulator.Errors import FormValidationError, ValidationError
Jean-Paul Smets's avatar
Jean-Paul Smets committed
37
from Products.Formulator.MethodField import BoundMethod
38
from Selection import Selection, DomainSelection
Jean-Paul Smets's avatar
Jean-Paul Smets committed
39
from DateTime import DateTime
Yoshinori Okuji's avatar
Yoshinori Okuji committed
40
from Products.ERP5Type.Utils import getPath, convertToUpperCase
41
from Products.ERP5Type.Document import newTempBase
42
from Products.CMFCore.utils import getToolByName
Sebastien Robin's avatar
Sebastien Robin committed
43
from copy import copy
44
from Products.ZSQLCatalog.zsqlbrain import ZSQLBrain
Jean-Paul Smets's avatar
Jean-Paul Smets committed
45 46 47

from Acquisition import aq_base, aq_inner, aq_parent, aq_self
from zLOG import LOG
Yoshinori Okuji's avatar
Yoshinori Okuji committed
48
from ZODB.POSException import ConflictError
Jean-Paul Smets's avatar
Jean-Paul Smets committed
49

50
from Globals import InitializeClass, Persistent, Acquisition, get_request
51 52
from Products.PythonScripts.Utility import allow_class

53
import random
54
import md5
55
import cgi
56

57 58 59 60
class ObjectValuesWrapper:
  """This class wraps objectValues so that objectValues behaves like portal_catalog.
  """
  method_name = __name__ = 'objectValues'
61

62 63
  def __init__(self, context):
    self.context = context
64

65 66 67 68 69 70 71 72 73
  def __call__(self, *args, **kw):
    brain_list = []
    for obj in self.context.objectValues(*args, **kw):
      brain = ZSQLBrain(None, None).__of__(obj)
      brain.uid = obj.getUid()
      brain.path = obj.getPath()
      brain_list.append(brain)
    return brain_list

74 75 76 77 78 79
def getAsList(a):
  l = []
  for e in a:
    l.append(e)
  return l

80 81 82 83
def makeTreeBody(form = None, root_dict = None, domain_path = '',
                 depth = 0, total_depth = None, unfolded_list = (),
                 form_id = None, selection_name = None,
                 base_category = None):
84 85
  """
    This method builds a report tree
86

87
    domain_path  --    ('region', 'skill', 'group', 'group', 'region')
88

89
    root -- {'region': <instance>, 'group'; instance}
90 91

  """
92 93 94
  #LOG('makeTreeBody root_dict', 0, str(root_dict))
  #LOG('makeTreeBody domain_path', 0, str(domain_path))
  #LOG('makeTreeBody unfolded_list', 0, str(unfolded_list))
95

96
  #LOG('makeTreeBody', 0, 'form = %r, root_dict = %r, domain_path = %r, depth = %r, total_depth = %r, unfolded_list = %r, form_id = %r, selection_name = %r, base_category = %r' % (form, root_dict, domain_path, depth, total_depth, unfolded_list, form_id, selection_name, base_category))
97 98
  if total_depth is None:
    total_depth = max(1, len(unfolded_list))
99

100 101 102 103 104
  if isinstance(domain_path, str):
    domain_path = domain_path.split('/')

  if form_id is None:
    form_id = form.id
105

106
  portal_categories = getattr(form, 'portal_categories', None)
107
  portal_domains = getattr(form, 'portal_domains', None)
108
  portal_object = form.portal_url.getPortalObject()
109

110
  if base_category is None and len(domain_path):
111
    base_category = domain_path[0]
112

113 114
  if root_dict is None:
    root_dict = {}
115

116 117
  #LOG('makeTreeBody', 0, 'domain_path = %r, base_category = %r' % (domain_path, base_category))

118
  is_empty_level = 1
119
  category = base_category
120
  while is_empty_level:
121
    if category not in root_dict:
122 123
      root = None
      if portal_categories is not None:
124 125
        if category in portal_categories.objectIds():
          root = root_dict[category] = root_dict[None] = portal_categories[category]
126 127
          domain_path = domain_path[1:]
      if root is None and portal_domains is not None:
128 129
        if category in portal_domains.objectIds():
          root = root_dict[category] = root_dict[None] = portal_domains[category]
130 131 132 133 134 135 136
          domain_path = domain_path[1:]
      if root is None:
        try:
          root = root_dict[None] = portal_object.unrestrictedTraverse(domain_path)
        except KeyError:
          root = None
        domain_path = ()
137
    else:
138
      root = root_dict[None] = root_dict[category]
139
      if len(domain_path) >= 1:
140
        domain_path = domain_path[1:]
141 142
      else:
        domain_path = ()
143
    is_empty_level = root is not None and (root.objectCount() == 0) and (len(domain_path) != 0)
144 145
    if is_empty_level:
      category = domain_path[0]
146

147
  #LOG('makeTreeBody', 0, 'root = %r, depth = %r, category = %r' % (root, depth, category))
Jean-Paul Smets's avatar
Jean-Paul Smets committed
148
  tree_body = ''
149
  if root is None: return tree_body
150
  if hasattr(root, 'getChildDomainValueList'):
151
    oblist = root.getChildDomainValueList(root,depth=depth)
152 153 154
  else:
    oblist = root.objectValues()
  for o in oblist:
Yoshinori Okuji's avatar
Yoshinori Okuji committed
155
    tree_body += '<tr>' + '<td width="16" nowrap>' * depth
156 157 158 159 160 161
    relative_url = o.getRelativeUrl()
    if base_category is not None and not relative_url.startswith(base_category + '/'):
      url = '%s/%s' % (base_category, relative_url)
    else:
      url = relative_url
    if url in unfolded_list:
Yoshinori Okuji's avatar
Yoshinori Okuji committed
162
      tree_body += """<td nowrap valign="top" align="left" colspan="%s">
163
<a href="portal_selections/foldDomain?domain_url=%s&form_id=%s&list_selection_name=%s&domain_depth:int=%s" title="%s" >- <b>%s</b></a>
Yoshinori Okuji's avatar
Yoshinori Okuji committed
164
</td>""" % (total_depth - depth + 1, url, form_id, selection_name, depth, unicode(o.getTranslatedTitle(), 'utf8'), o.id)
165 166
      new_root_dict = root_dict.copy()
      new_root_dict[None] = new_root_dict[base_category] = o
167 168 169
      tree_body += makeTreeBody(form = form, root_dict = new_root_dict, domain_path = domain_path,
                                depth = depth + 1, total_depth = total_depth, unfolded_list = unfolded_list,
                                selection_name = selection_name, base_category = base_category)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
170
    else:
Yoshinori Okuji's avatar
Yoshinori Okuji committed
171
      tree_body += """<td nowrap valign="top" align="left" colspan="%s">
172
<a href="portal_selections/unfoldDomain?domain_url=%s&form_id=%s&list_selection_name=%s&domain_depth:int=%s" title="%s">+ %s</a>
Yoshinori Okuji's avatar
Yoshinori Okuji committed
173 174
</td>""" % (total_depth - depth + 1, url, form_id, selection_name, depth, unicode(o.getTranslatedTitle(), 'utf8'), o.id)
    tree_body += '</td>' * depth + '</tr>'
Jean-Paul Smets's avatar
Jean-Paul Smets committed
175 176 177

  return tree_body

178 179
_parent_domain_mark = '__parent'

180
def makeTreeList(here, form, root_dict, report_path, base_category, depth, unfolded_list, form_id, selection_name, report_depth, is_report_opened=1, sort_on = (('id', 'ASC'),)):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
181
  """
182
    (object, is_pure_summary, depth, is_open, select_domain_dict)
183

184
    select_domain_dict is a dictionary of  associative list of (id, domain)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
185
  """
186
  if type(report_path) is type('a'): report_path = report_path.split('/')
187

188
  portal_categories = getattr(form, 'portal_categories', None)
189
  portal_domains = getattr(form, 'portal_domains', None)
190
  portal_object = form.portal_url.getPortalObject()
191

192 193
  if len(report_path):
    base_category = report_path[0]
194

195 196
  if root_dict is None:
    root_dict = {}
197 198

  is_empty_level = 1
199 200 201 202 203
  while is_empty_level:
    if not root_dict.has_key(base_category):
      root = None
      if portal_categories is not None:
        if base_category in portal_categories.objectIds():
204 205 206 207
          if base_category == 'parent':
            # parent has a special treatment
            root = root_dict[base_category] = root_dict[None] = here
            report_path = report_path[1:]
208
          else:
209 210
            root = root_dict[base_category] = root_dict[None] = portal_categories[base_category]
            report_path = report_path[1:]
211
      if root is None and portal_domains is not None:
212

213 214 215 216 217 218 219 220 221
        if base_category in portal_domains.objectIds():
          root = root_dict[base_category] = root_dict[None] = portal_domains[base_category]
          report_path = report_path[1:]
      if root is None:
        try:
          root = root_dict[None] = portal_object.unrestrictedTraverse(report_path)
        except KeyError:
          root = None
        report_path = ()
222
    else:
223 224
      root = root_dict[None] = root_dict[base_category]
      report_path = report_path[1:]
225
    is_empty_level = (root is None or root.objectCount() == 0) and (len(report_path) != 0)
226 227
    if is_empty_level: base_category = report_path[0]

Jean-Paul Smets's avatar
Jean-Paul Smets committed
228
  tree_list = []
229
  if root is None: return tree_list
Jean-Paul Smets's avatar
Jean-Paul Smets committed
230

231
  if base_category == 'parent':
232 233
    if hasattr(aq_base(root), 'objectValues'):
      # If this is a folder, try to browse the hierarchy
234
      for zo in root.searchFolder(sort_on=sort_on):
235 236 237 238 239 240 241
        o = zo.getObject()
        if o is not None:
          new_root_dict = root_dict.copy()
          new_root_dict[None] = new_root_dict[base_category] = o
          selection_domain = DomainSelection(domain_dict = new_root_dict)
          if (report_depth is not None and depth <= (report_depth - 1)) or o.getRelativeUrl() in unfolded_list:
            exception_uid_list = [] # Object we do not want to display
242
            for sub_zo in o.searchFolder(sort_on=sort_on):
243 244
              sub_o = sub_zo.getObject()
              if sub_o is not None and hasattr(aq_base(root), 'objectValues'):
245
                exception_uid_list.append(sub_o.getUid())
246 247 248
            tree_list += [(o, 1, depth, 1, selection_domain, exception_uid_list)] # Summary (open)
            if is_report_opened :
              tree_list += [(o, 0, depth, 0, selection_domain, exception_uid_list)] # List (contents, closed, must be strict selection)
249
            tree_list += makeTreeList(here, form, new_root_dict, report_path, base_category, depth + 1, unfolded_list, form_id, selection_name, report_depth, is_report_opened=is_report_opened, sort_on=sort_on)
250 251
          else:
            tree_list += [(o, 1, depth, 0, selection_domain, ())] # Summary (closed)
252
  else:
253
    if hasattr(root, 'getChildDomainValueList'):
254
      oblist = root.getChildDomainValueList(root,depth=depth)
255 256 257
    else:
      oblist = root.objectValues()
    for o in oblist:
258 259 260 261
      new_root_dict = root_dict.copy()
      new_root_dict[None] = new_root_dict[base_category] = o
      selection_domain = DomainSelection(domain_dict = new_root_dict)
      if (report_depth is not None and depth <= (report_depth - 1)) or o.getRelativeUrl() in unfolded_list:
262
        tree_list += [(o, 1, depth, 1, selection_domain, None)] # Summary (open)
263
        if is_report_opened :
264
          tree_list += [(o, 0, depth, 0, selection_domain, None)] # List (contents, closed, must be strict selection)
265 266
        tree_list += makeTreeList(here, form, new_root_dict, report_path, base_category, depth + 1,
            unfolded_list, form_id, selection_name, report_depth,
267
            is_report_opened=is_report_opened, sort_on=sort_on)
268
      else:
269
        tree_list += [(o, 1, depth, 0, selection_domain, None)] # Summary (closed)
270

Jean-Paul Smets's avatar
Jean-Paul Smets committed
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
  return tree_list


class ListBoxWidget(Widget.Widget):
    """
        ListBox widget

        The ListBox widget allows to display a collection of objects in a form.
        The ListBox widget can be used for many applications:

        1- show the content of a folder by providing a list of meta_types
           and eventually a sort order

        2- show the content of a relation by providing the name of the relation,
           a list of meta_types and eventually a sort order

        3- show the result of a search request by selecting a query and
           providing parameters for that query (and eventually a sort order)

        In all 3 cases, a parameter to hold the current start item must be
        stored somewhere, typically in a selection object.

        Parameters in case 3 should stored in a selection object which allows a per user
        per PC storage.

        ListBox uses the following control variables

        - sort_by -- the id to sort results

        - sort_order -- the order of sorting
    """
302 303 304 305 306
    # Define Properties for ListBoxWidget.
    property_names = list(Widget.Widget.property_names)

    # Default has no meaning in ListBox.
    property_names.remove('default')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
307 308 309 310 311

    lines = fields.IntegerField('lines',
                                title='Lines',
                                description=(
        "The number of lines of this list. Required."),
312
                                default=20,
Jean-Paul Smets's avatar
Jean-Paul Smets committed
313
                                required=1)
314
    property_names.append('lines')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
315 316 317 318 319 320 321

    columns = fields.ListTextAreaField('columns',
                                 title="Columns",
                                 description=(
        "A list of attributes names to display. Required."),
                                 default=[],
                                 required=1)
322
    property_names.append('columns')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
323 324 325 326 327 328 329

    all_columns = fields.ListTextAreaField('all_columns',
                                 title="More Columns",
                                 description=(
        "An optional list of attributes names to display."),
                                 default=[],
                                 required=0)
330
    property_names.append('all_columns')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
331 332 333 334 335 336 337

    search_columns = fields.ListTextAreaField('search_columns',
                                 title="Searchable Columns",
                                 description=(
        "An optional list of columns to search."),
                                 default=[],
                                 required=0)
338
    property_names.append('search_columns')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
339

340 341 342 343 344 345
    sort_columns = fields.ListTextAreaField('sort_columns',
                                 title="Sortable Columns",
                                 description=(
        "An optional list of columns to sort."),
                                 default=[],
                                 required=0)
346
    property_names.append('sort_columns')
347

Jean-Paul Smets's avatar
Jean-Paul Smets committed
348 349 350 351 352
    sort = fields.ListTextAreaField('sort',
                                 title='Default Sort',
                                 description=('The default sort keys and order'),
                                 default=[],
                                 required=0)
353
    property_names.append('sort')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
354 355 356 357 358 359 360

    list_method = fields.MethodField('list_method',
                                 title='List Method',
                                 description=('The method to use to list'
                                              'objects'),
                                 default='',
                                 required=0)
361
    property_names.append('list_method')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
362

363 364 365 366 367 368
    count_method = fields.MethodField('count_method',
                                 title='Count Method',
                                 description=('The method to use to count'
                                              'objects'),
                                 default='',
                                 required=0)
369
    property_names.append('count_method')
370

Jean-Paul Smets's avatar
Jean-Paul Smets committed
371 372
    stat_method = fields.MethodField('stat_method',
                                 title='Stat Method',
373
                                 description=('The method to use to stat'
Jean-Paul Smets's avatar
Jean-Paul Smets committed
374 375 376
                                              'objects'),
                                 default='',
                                 required=0)
377
    property_names.append('stat_method')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
378 379 380 381 382 383 384

    selection_name = fields.StringField('selection_name',
                                 title='Selection Name',
                                 description=('The name of the selection to store'
                                              'params of selection'),
                                 default='',
                                 required=0)
385
    property_names.append('selection_name')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
386 387 388 389 390 391 392

    meta_types = fields.ListTextAreaField('meta_types',
                                 title="Meta Types",
                                 description=(
        "Meta Types of objects to list. Required."),
                                 default=[],
                                 required=0)
393
    property_names.append('meta_types')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
394 395 396 397 398 399 400

    portal_types = fields.ListTextAreaField('portal_types',
                                 title="Portal Types",
                                 description=(
        "Portal Types of objects to list. Required."),
                                 default=[],
                                 required=0)
401
    property_names.append('portal_types')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
402

403
    # XXX Do we still need this?
Jean-Paul Smets's avatar
Jean-Paul Smets committed
404 405 406 407 408 409
    default_params = fields.ListTextAreaField('default_params',
                                 title="Default Parameters",
                                 description=(
        "Default Parameters for the List Method."),
                                 default=[],
                                 required=0)
410
    property_names.append('default_params')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
411 412 413 414 415 416

    search = fields.CheckBoxField('search',
                                 title='Search Row',
                                 description=('Search Row'),
                                 default='',
                                 required=0)
417
    property_names.append('search')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
418 419 420 421 422 423

    select = fields.CheckBoxField('select',
                                 title='Select Column',
                                 description=('Select Column'),
                                 default='',
                                 required=0)
424
    property_names.append('select')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
425 426 427 428 429 430 431

    editable_columns = fields.ListTextAreaField('editable_columns',
                                 title="Editable Columns",
                                 description=(
        "An optional list of columns which can be modified."),
                                 default=[],
                                 required=0)
432
    property_names.append('editable_columns')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
433

434 435 436 437 438 439
    stat_columns = fields.ListTextAreaField('stat_columns',
                                 title="Stat Columns",
                                 description=(
        "An optional list of columns which can be used for statistics."),
                                 default=[],
                                 required=0)
440
    property_names.append('stat_columns')
441 442 443 444 445 446 447

    url_columns = fields.ListTextAreaField('url_columns',
                                 title="URL Columns",
                                 description=(
        "An optional list of columns which can provide a custom URL."),
                                 default=[],
                                 required=0)
448
    property_names.append('url_columns')
449

450
    # XXX do we still need this?
Jean-Paul Smets's avatar
Jean-Paul Smets committed
451 452 453 454 455 456
    global_attributes = fields.ListTextAreaField('global_attributes',
                                 title="Global Attributes",
                                 description=(
        "An optional list of attributes which are set by hidden fields and which are applied to each editable column."),
                                 default=[],
                                 required=0)
457
    property_names.append('global_attributes')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
458 459 460 461 462 463

    domain_tree = fields.CheckBoxField('domain_tree',
                                 title='Domain Tree',
                                 description=('Selection Tree'),
                                 default='',
                                 required=0)
464
    property_names.append('domain_tree')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
465 466 467 468 469 470 471

    domain_root_list = fields.ListTextAreaField('domain_root_list',
                                 title="Domain Root",
                                 description=(
        "A list of domains which define the possible root."),
                                 default=[],
                                 required=0)
472
    property_names.append('domain_root_list')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
473 474 475 476 477 478

    report_tree = fields.CheckBoxField('report_tree',
                                 title='Report Tree',
                                 description=('Report Tree'),
                                 default='',
                                 required=0)
479
    property_names.append('report_tree')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
480 481 482 483 484 485 486

    report_root_list = fields.ListTextAreaField('report_root_list',
                                 title="Report Root",
                                 description=(
        "A list of domains which define the possible root."),
                                 default=[],
                                 required=0)
487
    property_names.append('report_root_list')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
488 489 490 491 492

    list_action = fields.StringField('list_action',
                                 title='List Action',
                                 description=('The id of the object action'
                                              'to display the current list'),
493
                                 default='list',
Jean-Paul Smets's avatar
Jean-Paul Smets committed
494
                                 required=1)
495
    property_names.append('list_action')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
496

497 498 499 500
    def render_view(self, field, value, REQUEST=None, render_format='html', key='listbox'):
        """
          Returns
        """
501
        if REQUEST is None: REQUEST=get_request()
502 503
        return self.render(field, key, value, REQUEST, render_format=render_format)

504
    def render(self, field, key, value, REQUEST, render_format='html'):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
505 506 507
        """
          This is where most things happen. This method renders a list
          of items
508

Jean-Paul Smets's avatar
Jean-Paul Smets committed
509 510 511
          render_format allows to produce either HTML (default)
          or produce a generic 'list' format which can be converted by page templates
          or dtml into various formats (ex. PDF, CSV, OpenOffice, etc.)
512

Jean-Paul Smets's avatar
Jean-Paul Smets committed
513
          the 'list' format includes additional metainformation
514

Jean-Paul Smets's avatar
Jean-Paul Smets committed
515
          - depth in a report tree (ex. 0, 1, 2, etc.)
516

Jean-Paul Smets's avatar
Jean-Paul Smets committed
517
          - nature of the line (ex. stat or nonstat)
518

Jean-Paul Smets's avatar
Jean-Paul Smets committed
519
          - identification of the tree (ex. relative_url)
520

Jean-Paul Smets's avatar
Jean-Paul Smets committed
521
          - uid if any (to allow future import)
522

Jean-Paul Smets's avatar
Jean-Paul Smets committed
523
          - etc.
524

Jean-Paul Smets's avatar
Jean-Paul Smets committed
525
          which is intended to simplify operation with a spreadsheet or a pagetemplate
Jean-Paul Smets's avatar
Jean-Paul Smets committed
526
        """
527 528 529 530 531
        ###############################################################
        #
        # First, grasp and intialize the variables we may need later
        #
        ###############################################################
532
        #render_start = DateTime()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
533 534 535
        here = REQUEST['here']
        reset = REQUEST.get('reset', 0)
        form = field.aq_parent
536
        field_errors = REQUEST.get('field_errors',{});
Jean-Paul Smets's avatar
Jean-Paul Smets committed
537 538 539 540 541 542 543 544 545 546 547
        field_title = field.get_value('title')
        lines = field.get_value('lines')
        meta_types = field.get_value('meta_types')
        portal_types= field.get_value('portal_types')
        columns = field.get_value('columns')
        all_columns = field.get_value('all_columns')
        default_params = field.get_value('default_params')
        search = field.get_value('search')
        select = field.get_value('select')
        sort = field.get_value('sort')
        editable_columns = field.get_value('editable_columns')
548
        stat_columns = field.get_value('stat_columns')
Yoshinori Okuji's avatar
Yoshinori Okuji committed
549
        url_columns = field.get_value('url_columns')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
550
        search_columns = field.get_value('search_columns')
551
        sort_columns = field.get_value('sort_columns')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
552 553 554 555 556
        domain_tree = field.get_value('domain_tree')
        report_tree = field.get_value('report_tree')
        domain_root_list = field.get_value('domain_root_list')
        report_root_list = field.get_value('report_root_list')
        list_method = field.get_value('list_method')
557
        count_method = field.get_value('count_method')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
558
        stat_method = field.get_value('stat_method')
Sebastien Robin's avatar
Sebastien Robin committed
559
        selection_index = REQUEST.get('selection_index')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
560
        selection_name = field.get_value('selection_name')
Sebastien Robin's avatar
Sebastien Robin committed
561
        portal_url_string = getToolByName(here, 'portal_url')()
562
        portal_categories = getattr(form, 'portal_categories', None)
563 564
        portal_domains = getattr(form, 'portal_domains', None)
        portal_object = form.portal_url.getPortalObject()
Sebastien Robin's avatar
Sebastien Robin committed
565 566 567
        #selection_name = REQUEST.get('selection_name',None)
        #if selection_name is None:
        #  selection_name = str(random.randrange(1,2147483600))
568
        current_selection_name = REQUEST.get('selection_name','default')
569 570
        current_selection_index = REQUEST.get('selection_index', 0)
        report_depth = REQUEST.get('report_depth', None)
571 572 573 574 575
        list_action = here.absolute_url() + '/' + field.get_value('list_action')
        if list_action.find('?') < 0:
          list_action += '?reset=1'
        else:
          list_action += '&reset=1'
Sebastien Robin's avatar
Sebastien Robin committed
576
        object_list = []
577

578
        # Get translation methods
579
        translate = portal_object.Localizer.translate
Jean-Paul Smets's avatar
Jean-Paul Smets committed
580

581
        # Make sure list_result_item is defined
582
        list_result_item = []
583

584
        if render_format == 'list':
585 586 587
          # initialize the result
          listboxline_list = []

588 589 590
        # Make sure that the title is not UTF-8.
        field_title = unicode(field_title, 'utf-8')

591 592 593
        # Make sure that columns are not UTF-8.
        columns = [(str(cname[0]), unicode(cname[1], 'utf-8')) for cname in columns]

594 595 596
        # Make sure that columns are not UTF-8.
        domain_root_list = [(str(cname[0]), unicode(cname[1], 'utf-8')) for cname in domain_root_list]

597
        #LOG('Listbox',0,'search_columns1: %s' % str(search_columns))
Jean-Paul Smets's avatar
Jean-Paul Smets committed
598 599 600
        if search_columns == [] or search_columns is None or search_columns == '':
          # We will set it as the schema
          search_columns = map(lambda x: [x,x],here.portal_catalog.schema())
601
          #LOG('Listbox',0,'search_columns2: %s' % str(search_columns))
Jean-Paul Smets's avatar
Jean-Paul Smets committed
602 603
        search_columns_id_list = map(lambda x: x[0], search_columns)

604 605 606
        if sort_columns == [] or sort_columns is None or sort_columns == '':
          sort_columns = search_columns
        sort_columns_id_list = map(lambda x: x[0], sort_columns)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
607

608
        # Display statistics if the button Parameter exists or stat columns are defined explicitly.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
609 610
        filtered_actions = here.portal_actions.listFilteredActionsFor(here);
        object_ui = filtered_actions.has_key('object_ui')
611 612 613 614 615 616 617 618
        show_stat = (object_ui or stat_columns)

        # If nothing is specified to stat_columns, assume that all columns are available.
        # For compatibility, because there was no stat_columns before.
        if not stat_columns:
          stat_columns = []
          for column in all_columns:
            stat_columns.append((column[0], column[0]))
619 620
          for column in columns: # Sometimes, all_columns is not defined
            stat_columns.append((column[0], column[0]))
Jean-Paul Smets's avatar
Jean-Paul Smets committed
621

Yoshinori Okuji's avatar
Yoshinori Okuji committed
622 623 624
        if not url_columns:
          url_columns = []

Jean-Paul Smets's avatar
Jean-Paul Smets committed
625 626 627 628 629 630
        has_catalog_path = None
        for (k, v) in all_columns:
          if k == 'catalog.path' or k == 'path':
            has_catalog_path = k
            break

631 632
        selection = here.portal_selections.getSelectionFor(selection_name, REQUEST=REQUEST)
        # Create selection if needed, with default sort order
Jean-Paul Smets's avatar
Jean-Paul Smets committed
633
        if selection is None:
634
          selection = Selection(params=default_params, default_sort_on = sort)
635 636
        # Or make sure all sort arguments are valid
        else:
637 638 639
          # Reset Selection is needed
          if reset is not 0 and reset is not '0':
            here.portal_selections.setSelectionToAll(selection_name)
640 641 642 643
            here.portal_selections.setSelectionSortOrder(selection_name, sort_on = [])

          # Modify the default sort index every time, because it may change immediately.
          selection.edit(default_sort_on = sort)
644

645
          # Filter non searchable items
646
          sort_list = []
647
          fix_sort = 0
648
          for (k , v) in selection.sort_on: # XXX Access to selection - bad
649
            if k in sort_columns_id_list:
650
              sort_list.append((k,v))
651 652
            else:
              fix_sort = 1
653
          if fix_sort: selection.sort_on = sort_list # XXX Access to selection - bad
Jean-Paul Smets's avatar
Jean-Paul Smets committed
654

655
        if not hasattr(selection, 'flat_list_mode'):
656 657 658 659
          # initialisation of render mode. Choose flat_list_mode by default
          selection.edit(flat_list_mode=1,domain_tree_mode=0,report_tree_mode=0)
          #selection.edit(flat_list_mode=(not (domain_tree or
          # report_tree)),domain_tree_mode=domain_tree,report_tree_mode= report_tree)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
660

661
        #LOG('ListBox', 0, 'sort = %s, selection.selection_sort_on = %s' % (repr(sort), repr(selection.selection_sort_on)))
662
        # Selection
Yoshinori Okuji's avatar
Yoshinori Okuji committed
663
        #LOG("Selection",0,str(selection.__dict__))
Jean-Paul Smets's avatar
Jean-Paul Smets committed
664 665 666

        # Display choosen by the user

667
        if selection.flat_list_mode is not None:
668
          if selection.flat_list_mode == 1:
Jean-Paul Smets's avatar
Jean-Paul Smets committed
669 670
            domain_tree = 0
            report_tree = 0
671
          elif selection.domain_tree_mode == 1:
672 673 674 675
            # Only display domain if domain is defined
            if len(domain_root_list):
              domain_tree = 1
              report_tree = 0
676
            else:
677 678
              domain_tree = 0
              report_tree = 0
679
          elif selection.report_tree_mode == 1:
680 681 682 683
            # Only display report if report is defined
            if len(report_root_list):
              domain_tree = 0
              report_tree = 1
684
            else:
685 686
              domain_tree = 0
              report_tree = 0
Jean-Paul Smets's avatar
Jean-Paul Smets committed
687

688 689 690 691
        # In report tree mode, we want to remember if the items have to be displayed
        is_report_opened = REQUEST.get('is_report_opened', selection.isReportOpened())
        selection.edit(report_opened=is_report_opened)

692
        checked_uids = selection.getCheckedUids()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
        columns = here.portal_selections.getSelectionColumns(selection_name,
                                                columns=columns, REQUEST=REQUEST)


        editable_column_ids = map(lambda x: x[0], editable_columns)

        url = REQUEST.URL

        # Build the list of meta_types
        filtered_meta_types = map(lambda x: x[0], meta_types)
        if len(filtered_meta_types) == 0:
          filtered_meta_types = None

        # Build the list of meta_types
        filtered_portal_types = map(lambda x: x[0], portal_types)
        if len(filtered_portal_types) == 0:
            filtered_portal_types = None

        # Combine default values, selection values and REQUEST
712
        params = selection.getParams()
713 714 715 716 717 718 719
        if list_method not in (None, ''):
          # Only update params if list_method is defined
          # (ie. do not update params in listboxed intended to show a previously defined selection
          params.update(REQUEST.form)
          for (k,v) in default_params:
            if REQUEST.form.has_key(k):
              params[k] = REQUEST.form[k]
720
            params.setdefault(k, v)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
721

722
        # Allow overriding list_method, count_method and stat_method by params
723 724 725 726 727
        if params.has_key('list_method_id'):
          #try:
          list_method = getattr(here.portal_skins.local_list_method , params['list_method_id']) # Coramy specific
          #except:
          #  list_method = list_method
728 729 730 731 732
        if params.has_key('count_method_id'):
          #try:
          count_method = getattr(here.portal_skins.local_list_method , params['count_method_id']) # Coramy specific
          #except:
          #  list_method = list_method
733 734 735 736 737 738
        if params.has_key('stat_method_id'):
          #try:
          list_method = getattr(here.portal_skins.local_list_method , params['stat_method_id']) # Coramy specific
          #except:
          #  list_method = list_method

Jean-Paul Smets's avatar
Jean-Paul Smets committed
739
        # Set the params spec (this should change in the future)
740 741 742
        if list_method not in (None, ''):
          # Only update params if list_method is defined
          # (ie. do not update params in listboxed intended to show a previously defined selection
743 744 745 746
          if filtered_meta_types:
            params['meta_type'] = filtered_meta_types
          if filtered_portal_types:
            params['portal_type'] = filtered_portal_types
Jean-Paul Smets's avatar
Jean-Paul Smets committed
747

748
        ###############################################################
Yoshinori Okuji's avatar
Yoshinori Okuji committed
749
        #
Jean-Paul Smets's avatar
Jean-Paul Smets committed
750
        # Build the columns selections
751
        #
Jean-Paul Smets's avatar
Jean-Paul Smets committed
752 753 754
        # The idea is: instead of selecting *, listbox is able to
        # provide what should be selected. This should allow to reduce
        # the quantity of data transfered between MySQL and Zope
755 756
        #
        ###############################################################
Jean-Paul Smets's avatar
Jean-Paul Smets committed
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777
        extended_columns = []
        sql_columns = []
        for (sql, title) in columns:
          # original SQL id, Title, alias
          alias = string.split(sql,'.')
          alias = string.join(alias, '_')
          extended_columns += [(sql, title, alias)]
          if alias != sql:
            sql_columns += ['%s AS %s' % (sql, alias)]
          else:
            sql_columns += [alias]
        if has_catalog_path:
          alias = string.split(has_catalog_path,'.')
          alias = string.join(alias, '_')
          if alias != has_catalog_path:
            sql_columns += ['%s AS %s' % (has_catalog_path, alias)]
          else:
            sql_columns += [alias]
        sql_columns_string = string.join(sql_columns,' , ')
        params['select_columns'] = sql_columns_string

778
        ###############################################################
Yoshinori Okuji's avatar
Yoshinori Okuji committed
779
        #
Jean-Paul Smets's avatar
Jean-Paul Smets committed
780
        # Execute the query
Yoshinori Okuji's avatar
Yoshinori Okuji committed
781
        #
782
        ###############################################################
Yoshinori Okuji's avatar
Yoshinori Okuji committed
783

Jean-Paul Smets's avatar
Jean-Paul Smets committed
784
        kw = params
785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800

        # XXX Remove selection_expression if present.
        # This is necessary for now, because the actual selection expression in
        # search catalog does not take the requested columns into account. If
        # select_expression is passed, this can raise an exception, because stat
        # method sets select_expression, and this might cause duplicated column
        # names.
        #
        # In the future, this must be addressed in a clean way. selection_expression
        # should be used for search catalog, and search catalog should not use
        # catalog.* but only selection_expression. But this is a bit difficult,
        # because there is no simple way to distinguish queried columns from callable
        # objects in the current ListBox configuration.
        if 'select_expression' in kw:
          del kw['select_expression']

Jean-Paul Smets's avatar
Jean-Paul Smets committed
801 802
        if hasattr(list_method, 'method_name'):
          if list_method.method_name == 'objectValues':
803
            list_method = ObjectValuesWrapper(here)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
804 805
            kw = copy(params)
            kw['spec'] = filtered_meta_types
806
          else:
Jean-Paul Smets's avatar
Jean-Paul Smets committed
807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824
            # The Catalog Builds a Complex Query
            # So we should not pass too many variables
            kw = {}
            if REQUEST.form.has_key('portal_type'):
              kw['portal_type'] = REQUEST.form['portal_type']
            elif REQUEST.has_key('portal_type'):
              kw['portal_type'] = REQUEST['portal_type']
            elif filtered_portal_types is not None:
              kw['portal_type'] = filtered_portal_types
            elif filtered_meta_types is not None:
              kw['meta_type'] = filtered_meta_types
            elif kw.has_key('portal_type'):
              if kw['portal_type'] == '':
                del kw['portal_type']

            # Remove useless matter
            for cname in params.keys():
              if params[cname] != '' and params[cname]!=None:
825 826
                kw.setdefault(cname, params[cname])
            
Jean-Paul Smets's avatar
Jean-Paul Smets committed
827 828 829
            # Try to get the method through acquisition
            try:
              list_method = getattr(here, list_method.method_name)
Yoshinori Okuji's avatar
Yoshinori Okuji committed
830
            except (AttributeError, KeyError):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
831
              pass
832 833 834 835
        elif list_method in (None, ''): # Use current selection
          # Use previously used list method
          list_method = None

Jean-Paul Smets's avatar
Jean-Paul Smets committed
836

837 838 839 840 841 842 843 844 845
        # Lookup the count_method
        if hasattr(count_method, 'method_name'):
          if count_method.method_name == 'portal_catalog':
            # We use the catalog count results
            count_method = here.portal_catalog.countResults
          else:
            # Try to get the method through acquisition
            try:
              count_method = getattr(here, count_method.method_name)
Yoshinori Okuji's avatar
Yoshinori Okuji committed
846
            except (AttributeError, KeyError):
847 848 849 850
              count_method = None
        else:
          # No count method defined means that all objects must be retrieved.
          count_method = None
851 852


Jean-Paul Smets's avatar
Jean-Paul Smets committed
853 854 855 856 857 858 859 860
        # Lookup the stat_method
        if hasattr(stat_method, 'method_name'):
          if stat_method.method_name == 'objectValues':
            stat_method = None # Nothing to do in this case
            show_stat = 0
          elif stat_method.method_name == 'portal_catalog':
            # We use the catalog count results
            stat_method = here.portal_catalog.countResults
861
            show_stat = 1
Jean-Paul Smets's avatar
Jean-Paul Smets committed
862 863 864 865 866
          else:
            # Try to get the method through acquisition
            try:
              stat_method = getattr(here, stat_method.method_name)
              show_stat = 1
Yoshinori Okuji's avatar
Yoshinori Okuji committed
867
            except (AttributeError, KeyError):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
868 869 870
              show_stat = 0
              pass
        else:
871 872 873
          # No stat method defined means no statistics displayed
          stat_method = None
          show_stat = 0
Jean-Paul Smets's avatar
Jean-Paul Smets committed
874

875
          #LOG('ListBox', 0, 'domain_tree = %s, selection.getDomainPath() = %s, selection.getDomainList() = %s' % (repr(domain_tree), repr(selection.getDomainPath()), repr(selection.getDomainList())))
Jean-Paul Smets's avatar
Jean-Paul Smets committed
876
        if domain_tree:
877 878
          selection_domain_path = selection.getDomainPath()
          selection_domain_current = selection.getDomainList()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
879
          if len(selection_domain_current) > 0:
880
            root_dict = {}
881 882
            for domain in selection_domain_current:
              if type(domain) != type(''): continue # XXX workaround for a past bug in Selection
883 884 885 886
              root = None
              base_category = domain.split('/')[0]
              if portal_categories is not None:
                if base_category in portal_categories.objectIds():
887 888 889
                  root = portal_categories.restrictedTraverse(domain, None)
                  if root is not None :
                    root_dict[base_category] = root
890 891
              if root is None and portal_domains is not None:
                if base_category in portal_domains.objectIds():
892
                  base_domain = portal_domains.getDomainByPath(domain)
893
                  root = base_domain
894
                  root_dict[base_category] = base_domain.getRelativeUrl()
895 896 897 898 899
              if root is None:
                try:
                  root_dict[None] = portal_object.restrictedTraverse(domain)
                except KeyError:
                  root = None
900
              #LOG('domain_tree root aq_parent', 0, str(root_dict[base_category].aq_parent))
901 902
            selection.edit(domain = DomainSelection(domain_dict = root_dict).__of__(here))
            # LOG('selection.domain', 0, str(selection.domain.__dict__))
903 904
        else:
          selection.edit(domain = None)
905

906 907
        #LOG('ListBox', 0, 'list_method = %s, list_method.__dict__ = %s' % (repr(list_method), repr((list_method.__dict__))))

908
        ###############################################################
Yoshinori Okuji's avatar
Yoshinori Okuji committed
909
        #
910
        # Prepare the stat select_expression
Yoshinori Okuji's avatar
Yoshinori Okuji committed
911 912
        #
        ###############################################################
913
        select_expression = ''
914
        if show_stat:
Romain Courteaud's avatar
Romain Courteaud committed
915
          stats = here.portal_selections.getSelectionStats(
916
                                             selection_name,
Romain Courteaud's avatar
Romain Courteaud committed
917
                                             REQUEST=REQUEST)
918 919 920 921 922 923 924 925 926
          index = 0

          for (sql,title,alias) in extended_columns:
            # XXX This might be slow.
            for column in stat_columns:
              if column[0] == sql:
                break
            else:
              column = None
Romain Courteaud's avatar
Romain Courteaud committed
927
            if (column is not None) and (column[0] == column[1]):
928 929
              try:
                if stats[index] != ' ':
930
                  select_expression += '%s(%s) AS %s,' % (stats[index], sql,
Romain Courteaud's avatar
Romain Courteaud committed
931
                                                          alias)
932
                else:
Romain Courteaud's avatar
Romain Courteaud committed
933 934 935
                  select_expression += '\'&nbsp;\' AS %s,' % alias
              except IndexError:
                select_expression += '\'&nbsp;\' AS %s,' % alias
936 937 938
            index = index + 1

          select_expression = select_expression[:len(select_expression) - 1]
Yoshinori Okuji's avatar
Yoshinori Okuji committed
939

940 941 942 943 944 945 946 947 948 949 950
        ###############################################################
        #
        # Calculate list start temporarily
        #
        ###############################################################
        if render_format == 'list':
          start = 0
        else:
          try:
            start = REQUEST.get('list_start')
            start = int(start)
Yoshinori Okuji's avatar
Yoshinori Okuji committed
951
          except (TypeError, KeyError):
952
            start = params.get('list_start',0)
953 954
            if type(start) is type([]):
              start = start[0]
955 956
            start = int(start)
          start = max(start, 0)
957

958
        ###############################################################
Yoshinori Okuji's avatar
Yoshinori Okuji committed
959
        #
960 961 962 963 964
        # Build the report tree
        #
        # When we build the body, we have to go through all report lines
        #
        # Each report line is a tuple of the form:
965 966
        #
        # (section_id, is_summary, depth, object_list, object_list_size, is_open)
Yoshinori Okuji's avatar
Yoshinori Okuji committed
967 968
        #
        ###############################################################
Jean-Paul Smets's avatar
Jean-Paul Smets committed
969
        if report_tree:
970 971 972 973 974
          default_selection_report_path = report_root_list[0][0].split('/')[0]
          if default_selection_report_path in portal_categories.objectIds() or \
            (portal_domains is not None and default_selection_report_path in portal_domains.objectIds()):
            pass
          else:
975
            default_selection_report_path = report_root_list[0][0]
976
          selection_report_path = selection.getReportPath(default = default_selection_report_path)
977 978
          if report_depth is not None:
            selection_report_current = ()
979
          else:
980
            selection_report_current = selection.getReportList()
981
          report_tree_list = makeTreeList(here, form, None, selection_report_path, None,
982
                                          0, selection_report_current, form.id, selection_name, report_depth,
983
                                          is_report_opened, sort_on=selection.sort_on)
984 985

          # Update report list if report_depth was specified
986 987 988
          if report_depth is not None:
            report_list = map(lambda s:s[0].getRelativeUrl(), report_tree_list)
            selection.edit(report_list=report_list)
989

Jean-Paul Smets's avatar
Jean-Paul Smets committed
990 991 992
          report_sections = []
          #LOG("Report Tree",0,str(report_tree_list))
          for s in report_tree_list:
993
            # Prepare query by defining selection report object
994
            #if s[4] is not _parent_domain_mark:
995
            selection.edit(report = s[4])
Jean-Paul Smets's avatar
Jean-Paul Smets committed
996
            if s[1]:
997 998 999
              # Push new select_expression
              original_select_expression = kw.get('select_expression')
              kw['select_expression'] = select_expression
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1000
              selection.edit( params = kw )
1001
              #LOG('ListBox 569', 0, str((selection_name, selection.__dict__)))
1002
              stat_temp = selection(method = stat_method, context=here, REQUEST=REQUEST)
1003 1004 1005 1006 1007
              # Pop new select_expression
              if original_select_expression is None:
                del kw['select_expression']
              else:
                kw['select_expression'] = original_select_expression
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1008

1009
            if s[1] and show_stat:
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
              # stat_result is a list
              # we want now to make it a dictionnary
              # Is this a report line
              # object_stat = ....
              stat_result = {}
              index = 1

              for (k,v) in columns:
                try:
                  stat_result[k] = str(stat_temp[0][index])
                except IndexError:
                  stat_result[k] = ''
                index = index + 1

              stat_context = s[0].asContext(**stat_result)
1025 1026
              absolute_url_txt = s[0].absolute_url()
              stat_context.absolute_url = lambda: absolute_url_txt
1027
              stat_context.domain_url = s[0].getRelativeUrl()
1028
              section_title = s[0].getTitleOrId()
1029
              section_title = translate('erp5_content', section_title, default=section_title.decode('utf-8'))
1030 1031
              if type(section_title) == type(u''):
                section_title = section_title.encode('utf-8')
1032
              report_sections += [(s[0].getTitleOrId(), 1, s[2], [stat_context], 1, s[3], s[4], stat_context, 0)]
1033
              #                 report id, is_summary, depth, object_list, object_list_len, XX, XX, report_object, start, stop
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1034 1035 1036
            else:
              # Prepare query
              selection.edit( params = kw )
1037
              if list_method not in (None, ''):
1038 1039 1040
                #if s[4] is not _parent_domain_mark:
                object_list = selection(method = list_method, context=here, REQUEST=REQUEST)
                #else:
1041
                #  object_list = [s[0]]
1042 1043
              else:
                # If list_method is None, use already selected values.
1044
                #if s[4] is not _parent_domain_mark:
1045
                object_list = here.portal_selections.getSelectionValueList(selection_name,
1046
                                                                context=here, REQUEST=REQUEST)
1047
                #else:
1048
                #  object_list = [s[0]]
1049
#               # PERFORMANCE ? is len(object_list) fast enough ?
1050 1051 1052 1053 1054 1055 1056 1057 1058
              exception_uid_list = s[5]
              if exception_uid_list is not None:
                # Filter folders if this is a parent tree
                new_object_list = []
                for o in object_list:
                  #LOG('exception_uid_list', 0, '%s %s' % (o.getUid(), exception_uid_list))
                  if o.getUid() not in exception_uid_list:
                    new_object_list.append(o)
                object_list = new_object_list
1059
              object_list_len = len(object_list)
1060 1061 1062
              if not s[1]:
                if show_stat:
                  report_sections += [ (None, 0, s[2], object_list, object_list_len, s[3], s[4], None, 0) ]
1063
              else:
1064
                stat_context = s[0].asContext()
1065 1066
                absolute_url_txt = s[0].absolute_url()
                stat_context.absolute_url = lambda : absolute_url_txt
1067 1068 1069 1070
                stat_context.domain_url = s[0].getRelativeUrl()
                if object_list_len and s[3]:
                  # Display object data at same level as category selector
                  # If this domain is open
1071
                  report_sections += [ (s[0].getTitleOrId(), 0, s[2], [object_list[0]], 1, s[3], s[4], stat_context, 0) ]
1072
                  report_sections += [ (None, 0, s[2], object_list, object_list_len - 1, s[3], s[4], None, 1) ]
1073
                else:
1074 1075
                  if exception_uid_list is not None:
                    # Display current parent domain
1076
                    report_sections += [ (s[0].getTitleOrId(), 0, s[2], [s[0]], 1, s[3], s[4], stat_context, 0) ]
1077
                  else:
1078
                    # No data to display
1079
                    report_sections += [ (s[0].getTitleOrId(), 0, s[2], [None], 1, s[3], s[4], stat_context, 0) ]
1080 1081 1082

          # Reset original value
          selection.edit(report = None)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1083
        else:
1084
          #LOG('ListBox 1054', 0, str((selection_name, selection.__dict__, selection)))
1085
          if list_method is not None:
1086
            if count_method is not None and not selection.invert_mode and render_format == 'html':
1087 1088 1089 1090
              #LOG('ListBox', 0, 'use count_method %r' % count_method)
              # If the count method is available, get only required objects.
              selection.edit( params = kw, report = None )
              count = selection(method = count_method, context=here, REQUEST=REQUEST)
1091
              object_list_len = int(count[0][0])
1092 1093
              if start > object_list_len:
                start = object_list_len
1094 1095 1096 1097 1098 1099
              start -= (start % lines)
              kw['limit'] = (start, lines)
              selection.edit( params = kw, report = None )
              object_list = selection(method = list_method, context=here, REQUEST=REQUEST)
              del kw['limit']
              selection.edit( params = kw, report = None ) # XXX Necessary?
1100 1101 1102 1103 1104 1105
              # For convenience, add padding into the list of objects with None.
              object_list = [None] * start + list(object_list) + [None] * (object_list_len - len(object_list) - start)
            else:
              selection.edit( params = kw, report = None )
              object_list = selection(method = list_method, context=here, REQUEST=REQUEST)
              object_list_len = len(object_list)
1106
          else:
1107
            selection.edit( params = kw, report = None )
1108 1109
            # If list_method is None, use already selected values.
            object_list = here.portal_selections.getSelectionValueList(selection_name, context=here, REQUEST=REQUEST)
1110 1111
            # PERFORMANCE PROBLEM ? is len(object_list) fast enough ?
            object_list_len = len(object_list)
1112
          report_sections = ( (None, 0, 0, object_list, object_list_len, 0, None, None, 0),  )
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1113

Yoshinori Okuji's avatar
Yoshinori Okuji committed
1114

1115
        ###############################################################
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1116 1117 1118
        #
        # Build an md5 signature of the selection
        #
1119
        # It is calculated based on the selection uid list
Sebastien Robin's avatar
Sebastien Robin committed
1120 1121 1122 1123 1124 1125
        # It is used in order to do some checks in scripts.
        # For example, if we do delete objects, then we do have a list of
        # objects to delete, but it is possible that on another tab the selection
        # change, and then when we confirm the deletion, we don't delete what
        # we want, so this is really dangerous. with this md5 we can check if the
        # selection is the same
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1126 1127 1128
        #
        ###############################################################

1129
        # XXX To avoid the difference of the string representations of int and long,
1130
        # convert each element to a string.
1131 1132 1133
        object_uid_list = [str(getattr(x, 'uid', None)) for x in object_list]
        object_uid_list.sort()
        md5_string = md5.new(str(object_uid_list)).hexdigest()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1134

1135
        ###############################################################
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1136
        #
1137
        # Calculate list start and stop
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1138
        #
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1139 1140 1141
        # Build the real list by slicing it
        # PERFORMANCE ANALYSIS: the result of the query should be
        # if possible a lazy sequence
1142
        #
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1143 1144
        ###############################################################

1145
        #LOG("Selection", 0, str(selection.__dict__))
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1146 1147 1148
        total_size = 0
        for s in report_sections:
          total_size += s[4]
1149
        if render_format == 'list' or lines == 0:
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1150 1151
          end = total_size
          total_pages = 1
1152
          current_page = 0
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1153
        else:
1154 1155 1156 1157 1158 1159 1160 1161 1162
          end = min(start + lines, total_size)
          #object_list = object_list[start:end]
          total_pages = int(max(total_size-1,0) / lines) + 1
          current_page = int(start / lines)
          start = min(start, max(0, total_pages * lines - lines) )
          kw['list_start'] = start
          kw['list_lines'] = lines

        ###############################################################
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1163
        #
1164
        # Store new selection values
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1165
        #
1166 1167
        # Store the resulting selection if list_method is not None and render_format is not list
        #
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1168
        ###############################################################
1169
        if list_method is not None and render_format != 'list':
1170 1171
          try:
            method_path = getPath(here) + '/' + list_method.method_name
1172
            #LOG('ListBox', 0, 'method_path = %s, getPath = %s, list_method.method_name = %s' % (repr(method_path), repr(getPath(here)), repr(list_method.method_name)))
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1173
          except AttributeError:
1174
            method_path = getPath(here) + '/' + list_method.__name__
1175
            #LOG('ListBox', 0, 'method_path = %s, getPath = %s, list_method.__name__ = %s' % (repr(method_path), repr(getPath(here)), repr(list_method.__name__)))
1176 1177 1178 1179 1180
          # Sometimes the seltion name is a list ??? Why ????
          if type(current_selection_name) in (type(()),type([])):
            current_selection_name = current_selection_name[0]
          list_url =  url+'?selection_name='+current_selection_name+'&selection_index='+str(selection_index)
          selection.edit( method_path= method_path, params = kw, list_url = list_url)
1181 1182
          #LOG("Selection kw", 0, str(selection.selection_params))
          here.portal_selections.setSelectionFor(selection_name, selection, REQUEST=REQUEST)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1183

1184
        ###############################################################
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1185
        #
1186
        # Build HTML header and footer
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1187
        #
1188
        ###############################################################
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1189

Jean-Paul Smets's avatar
Jean-Paul Smets committed
1190 1191 1192 1193
        # Provide the selection name
        selection_line = """\
<input type="hidden" name="list_selection_name" value="%s" />
""" % selection_name
Sebastien Robin's avatar
Sebastien Robin committed
1194 1195 1196
        selection_line +="""\
<input type="hidden" name="md5_object_uid_list" value="%s" />
""" % md5_string
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1197 1198

        # Create the Page Selector
1199 1200
        pages_list = []
        pages_list_append = pages_list.append
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1201
        if start == 0:
1202
          pages_list_append("""\
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1203 1204 1205
   <td nowrap valign="middle" align="center">
   </td>
   <td nowrap valign="middle" align="center">
1206
    <select id="listbox_page_selection" name="list_start" title="%s" size="1"
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1207
      onChange="submitAction(this.form,'%s/portal_selections/setPage')">
1208
""" % (translate('erp5_ui', 'Change Page'), REQUEST.URL1))
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1209
        else:
1210
          pages_list_append("""\
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1211
   <td nowrap valign="middle" align="center">
1212
    <input id="listbox_previous_page" type="image" src="%s/images/1leftarrowv.png"
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1213
      title="%s" name="portal_selections/previousPage:method" border="0" />
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1214 1215
   </td>
   <td nowrap valign="middle" align="center">
1216
    <select id="listbox_page_selection" name="list_start" title="%s" size="1"
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1217
      onChange="submitAction(this.form,'%s/portal_selections/setPage')">
1218 1219
""" % (portal_url_string, translate('erp5_ui', 'Previous Page'),
       translate('erp5_ui', 'Change Page'), REQUEST.URL1))
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1220 1221
        for p in range(0, total_pages):
          if p == current_page:
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1222
            selected = 'selected'
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1223
          else:
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1224
            selected = ''
1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235
          pages_list_append( '<option %s value="%s">%s</option>\n' \
                             % ( selected
                               , p * lines
                               , translate( 'erp5_ui'
                                          , '${page} of ${total_pages}'
                                          , mapping = { 'page'       : p+1
                                                      , 'total_pages': total_pages
                                                      }
                                          )
                               )
                           )
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1236
        if current_page == total_pages - 1:
1237
          pages_list_append("""\
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1238 1239 1240 1241
    </select>
   </td>
   <td nowrap valign="middle" align="center">
   </td>
1242
""")
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1243
        else:
1244
          pages_list_append("""\
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1245 1246 1247
    </select>
   </td>
   <td nowrap valign="middle" align="center">
1248
    <input id="listbox_next_page" type="image" src="%s/images/1rightarrowv.png"
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1249
      title="%s" name="portal_selections/nextPage:method" border="0" />
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1250
   </td>
1251
""" % (portal_url_string, translate('erp5_ui', 'Next Page')))
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1252 1253 1254
        # Create the header of the table - this should probably become DTML
        # Create also View Selector which enables to switch from a view mode
        # to another directly from the listbox
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1255
        #LOG('ListBox', 0, 'field_title = %s, translate(\'ui\', field_title) + %s' % (repr(field_title), repr(translate('ui', field_title))))
1256
        pages = ''.join(pages_list)
1257 1258 1259
        format_dict = { 'portal_url_string': portal_url_string
                      , 'list_action'      : list_action
                      , 'selection_name'   : selection_name
1260
                      , 'field_title'      : translate('erp5_ui', field_title)
1261
                      , 'pages'            : pages
1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274
                      , 'record_number'    : translate( 'erp5_ui'
                                                      , '${number} record(s)'
                                                      , default='%s record(s)' % total_size
                                                      , mapping={'number': str(total_size)}
                                                      )
                      , 'item_number'      : translate( 'erp5_ui'
                                                      , '${number} item(s) selected'
                                                      , default='%s item(s) selected' % len(checked_uids)
                                                      , mapping={'number': str(len(checked_uids))}
                                                      )
                      , 'flat_list_title'  : translate('erp5_ui', 'Flat List')
                      , 'report_tree_title': translate('erp5_ui', 'Report Tree')
                      , 'domain_tree_title': translate('erp5_ui', 'Domain Tree')
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1275
                      }
1276 1277 1278
        header_list = []
        header_list_append = header_list.append
        header_list_append("""\
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1279 1280 1281 1282
<!-- List Summary -->
<div class="ListSummary">
 <table border="0" cellpadding="0" cellspacing="0">
  <tr height="10">
1283
   <td height="10"><img src="%(portal_url_string)s/images/Left.png" border="0"/></td>
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1284
   <td class="Top" colspan="2" height="10">
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1285
    <img src="%(portal_url_string)s/images/spacer.png" width="5" height="10" border="0"
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1286 1287
      alt="spacer"/></td>
   <td class="Top" colspan="3" height="10">
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1288
    <img src="%(portal_url_string)s/images/spacer.png" width="5" height="10" border="0"
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1289 1290 1291
      alt="spacer"/>
   </td>
  </tr>
1292
  <tr id="listbox_title_line">
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1293
   <td class="Left" width="17">
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1294
    <img src="%(portal_url_string)s/images/spacer.png" width="5" height="5" border="0"
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1295 1296 1297
        alt="spacer"/>
   </td>
   <td valign="middle" nowrap>
1298
""" % format_dict)
1299
        if len(report_root_list) or len(domain_root_list):
1300
          header_list_append("""
1301
    <input type="image" src="%(portal_url_string)s/images/text_block.png" id="listbox_flat_list_mode"
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1302
       title="%(flat_list_title)s" name="portal_selections/setFlatListMode:method" value="1" border="0" alt="img"/">
1303
""" % format_dict)
1304
        if len(report_root_list):
1305
          header_list_append("""
1306
    <input type="image" src="%(portal_url_string)s/images/view_tree.png" id="listbox_report_tree_mode"
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1307
       title="%(report_tree_title)s" name="portal_selections/setReportTreeMode:method" value="1" border="0" alt="img"/">
1308
""" % format_dict)
1309
        if len(domain_root_list):
1310
          header_list_append("""
1311
        <input type="image" src="%(portal_url_string)s/images/view_choose.png" id="listbox_domain_tree_mode"
1312
       title="%(domain_tree_title)s" name="portal_selections/setDomainTreeMode:method" value="1" border="0" alt="img"/">
1313 1314
""" % format_dict)
        header_list_append("""
1315
       </td>
1316 1317 1318
   <td width="100%%" valign="middle">&nbsp; <a id="listbox_title" href="%(list_action)s">%(field_title)s</a>:
        <span id="listbox_record_number">%(record_number)s</span>
        - <span id="listbox_item_number">%(item_number)s</span>
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1319
   </td>
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1320
   %(pages)s
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1321 1322 1323 1324 1325 1326
  </tr>
 </table>
</div>
<!-- List Content -->
<div class="ListContent">
 <table cellpadding="0" cellspacing="0" border="0">
1327
""" % format_dict)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343

        # pages

        # Create the footer. This should be replaced by DTML
        # And work as some kind of parameter

        footer = """\
   <tr >
    <td colspan="%s" width="50" align="center" valign="middle"
        class="DataA">
    </td>
   </tr>
  </table>
 </div>
""" % (len(columns))

1344
        header = ''.join(header_list)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357
        # Create the header of the table with the name of the columns
        # Create also Report Tree Column if needed
        if report_tree:
          report_tree_options = ''
          for c in report_root_list:
            if c[0] == selection_report_path:
              report_tree_options += """<option selected value="%s">%s</option>\n""" % (c[0], c[1])
            else:
              report_tree_options += """<option value="%s">%s</option>\n""" % (c[0], c[1])
          report_popup = """<select name="report_root_url"
onChange="submitAction(this.form,'%s/portal_selections/setReportRoot')">
        %s</select>""" % (here.getUrl(),report_tree_options)
          report_popup = """
1358
  <td class="Data" width="50" align="left" valign="middle">
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1359 1360 1361 1362 1363 1364
  %s
  </td>
""" % report_popup
        else:
          report_popup = ''

1365 1366
        list_header_list = []
        list_header_list_append = list_header_list.append
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1367
        if select:
1368 1369
          format_dict = { 'portal_url_string': portal_url_string
                        , 'report_popup'     : report_popup
1370 1371
                        , 'check_all_title'  : translate('erp5_ui', 'Check All')
                        , 'uncheck_all_title': translate('erp5_ui', 'Uncheck All')
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1372
                        }
1373
          list_header_list_append("""\
1374 1375 1376
<tr id="listbox_label_line">%(report_popup)s
  <td class="Data" width="50" align="center" valign="middle">
    <input id="listbox_check_all" type="image" name="portal_selections/checkAll:method" value="1"
1377
      src="%(portal_url_string)s/images/checkall.png" border="0" alt="Check All" title="%(check_all_title)s" />
1378
    <input id="listbox_uncheck_all" type="image" name="portal_selections/uncheckAll:method" value="1"
1379
      src="%(portal_url_string)s/images/decheckall.png" border="0" alt="Uncheck All" title="%(uncheck_all_title)s" />
1380
  </td>
1381
""" % format_dict)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1382
        else:
1383
          list_header_list_append("""\
1384
<tr id="listbox_label_line">%s
1385
""" % report_popup)
1386

Jean-Paul Smets's avatar
Jean-Paul Smets committed
1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397
        # csort is a list of couples
        # we should convert it into a dict because a list of couples would need
        # a loop in each loop
        sort_dict = {}
        csort = here.portal_selections.getSelectionSortOrder(selection_name)
        for index in csort:
          sort_dict[index[0]] = index[1]

        for cname in columns:
          if sort_dict.has_key(cname[0]):
            if sort_dict[cname[0]] == 'ascending':
1398
              img = '<img src="%s/images/1bottomarrow.png" alt="Ascending display"/>' % portal_url_string
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1399
            elif sort_dict[cname[0]] == 'descending':
1400
              img = '<img src="%s/images/1toparrow.png" alt="Descending display"/>' % portal_url_string
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1401 1402 1403 1404
            else:
              img = ''
          else:
            img = ''
1405
          #LOG('ListBox', 0, 'cname = %r' % (cname,))
1406
          if cname[0] in sort_columns_id_list:
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1407
            #LOG('ListBox', 0, 'str(cname[1]) = %s, translate(\'ui\',str(cname[1])) = %s' % (repr(str(cname[1])), repr(translate('ui',str(cname[1])))))
1408
            list_header_list_append("<td class=\"Data\"><a href=\"%s/portal_selections/setSelectionQuickSortOrder?selection_name=%s&sort_on=%s\">%s</a> %s</td>\n" %
1409
                (here.absolute_url(), str(selection_name), cname[0], translate('erp5_ui', cname[1]), img))
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1410
          else:
1411
            list_header_list_append("<td class=\"Data\">%s</td>\n" % translate('erp5_ui', cname[1]))
1412 1413
        list_header_list_append("</tr>")
        list_header = ''.join(list_header_list)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1414

1415 1416
        # Create report depth_selector
        if report_tree:
1417
          depth_selector_list = []
1418
          depth_selector_list_append = depth_selector_list.append
1419 1420
          for i in range(0,6):
            # XXX We may lose previous list information
1421 1422
            depth_selector_list_append("""&nbsp;<a href="%s/%s?selection_name=%s&selection_index=%s&report_depth:int=%s">%s</a>""" % \
                                      (here.absolute_url(), form.id, current_selection_name, current_selection_index , i, i))
1423
          # In report mode, we may want to hide items, and only display stat lines.
1424 1425
          depth_selector_list_append("""&nbsp;-&nbsp;<a href="%s/%s?selection_name=%s&selection_index=%s&is_report_opened:int=%s">%s</a>""" % \
                                    (here.absolute_url(), form.id, current_selection_name, current_selection_index , 1 - is_report_opened, is_report_opened and 'Hide' or 'Show'))
1426

1427
          depth_selector = ''.join(depth_selector_list)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1428
        # Create the search row of the table with the name of the columns
1429 1430
        list_search_list = []
        list_search_append = list_search_list.append
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1431 1432
        if search:
          if report_tree:
1433
            # Add empty column for report
1434
            report_search = """<td class="Data" width="50" align="left" valign="middle">%s</td>""" % depth_selector
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1435 1436 1437 1438
          else:
            report_search = ""

          if select:
1439
            list_search_append("""\
1440
  <tr id="listbox_search_line">
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1441 1442
   %s
   <td class="Data" width="50" align="center" valign="middle">
1443
     <input id="listbox_select" type="image" src="%s/images/exec16.png" title="%s" alt="Action" name="Base_doSelect:method" />
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1444
   </td>
1445
""" % (report_search,portal_url_string,translate('erp5_ui', 'Action'))) # XXX Action? Is this word appropriate here?
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1446
          else:
1447
            list_search_append("""\
1448
  <tr id="listbox_search_line">
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1449
  %s
1450
""" % report_search)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1451 1452 1453

          for cname in extended_columns:
            if cname[0] in search_columns_id_list:
1454
              alias = str(cname[0])
1455 1456
              if type(alias) == type(''):
                alias = unicode(alias, 'utf-8')
1457 1458 1459
              param = params.get(alias,'')
              if type(param) == type(''):
                param = unicode(param, 'utf-8')
1460
              search_field_id = 'search_%s_%s' % (field.id, alias)
1461 1462 1463 1464 1465
              if form.has_field(search_field_id):
                # First look for a search field
                search_field = form.get_field(search_field_id)
                search_field_html = search_field.render(value = param, key=alias)
              else:
1466
                # Then create default rendering
1467
                search_field_html = """<input name="%s" size="8" value="%s" />""" % (alias, param)
1468
              list_search_append("""\
1469
     <td class="DataB">
1470
       <font size="-3">%s</font>
1471
     </td>
1472
""" % search_field_html)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1473
            else:
1474
              list_search_append("<td class=\"DataB\"></td> ")
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1475

1476
          list_search_append("</tr>")
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1477
        else:
1478
          if report_tree:
1479 1480
            list_search_append( """<td class="Data" width="50" align="left" valign="middle" colspan="%s">%s</td>""" % \
                              (len(extended_columns) + select + 1, depth_selector))
1481
          else:
1482 1483
            list_search_append('')
        list_search = ''.join(list_search_list)
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1484

1485 1486 1487
        # Build the tuple of columns
        if render_format == 'list':
          c_name_list = []
1488
          c_property_id_list = []
1489 1490
          for cname in columns:
            c_name_list.append(cname[1])
1491
            c_property_id_list.append(cname[0])
1492 1493

        ###############################################################
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1494
        #
1495
        # Build lines
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1496
        #
1497
        ###############################################################
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1498

1499

Jean-Paul Smets's avatar
Jean-Paul Smets committed
1500
        # Build Lines
1501 1502
        list_body_list = []
        list_body_append = list_body_list.append
1503

1504
        if render_format == 'list':
1505 1506 1507 1508
          # initialize the title line
          title_listboxline = ListBoxLine()
          title_listboxline.markTitleLine()
          for cname in columns:
1509
            title_listboxline.addColumn( cname[0], cname[1].encode('utf-8'))
1510
          listboxline_list.append(title_listboxline)
1511

Jean-Paul Smets's avatar
Jean-Paul Smets committed
1512 1513
        section_index = 0
        current_section_base_index = 0
1514 1515 1516 1517
        if len(report_sections) > section_index:
          current_section = report_sections[section_index]
        elif len(report_sections):
          current_section = report_sections[0]
1518 1519
        else:
          current_section = None
1520 1521 1522
        if current_section is not None:
          current_section_size = current_section[4]
          object_list = current_section[3]
1523 1524
          stat_context = current_section[7]
          index_shift = current_section[8]
1525
          #if current_section is not None:
1526
          for i in range(start,end):
1527 1528 1529 1530 1531

            if render_format == 'list':
              # Create a ListBoxLine object
              current_listboxline = ListBoxLine()
              current_listboxline.markDataLine()
1532

1533

1534 1535
            # Set the selection index.
            selection.edit(index = i)
1536

1537 1538 1539 1540 1541 1542 1543
            # Make sure we go to the right section
            while current_section_base_index + current_section_size <= i:
              current_section_base_index += current_section[4]
              section_index += 1
              current_section = report_sections[section_index]
              current_section_size = current_section[4]
              object_list = current_section[3]
1544 1545
              stat_context = current_section[7]
              index_shift = current_section[8]
1546

1547

1548
            is_summary = current_section[1] # Update summary type
1549

1550 1551 1552
            # Define the CSS
            if not (i - start) % 2:
              td_css = 'DataA'
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1553
            else:
1554
              td_css = 'DataB'
1555

1556
            list_body_append('<tr id="listbox_data_line_%d" class="%s">' % (i - start, td_css))
1557 1558 1559
            o = object_list[i - current_section_base_index + index_shift] # FASTER PERFORMANCE
            real_o = None

1560
            list_body_append(\
1561
  """<input type="hidden" value="%s" name="%s_uid:list"/>
1562
  """ % ( getattr(o, 'uid', '') , field.id )) # What happens if we list instances which are not instances of Base XXX
1563

1564 1565
            section_char = ''
            if report_tree:
1566
              if is_summary or current_section[0] is not None:
1567 1568
                # This is a summary
                section_name = current_section[0]
1569
              else:
1570
                section_name = ''
1571

1572 1573 1574
              if current_section[5]:
                if section_name != '':
                  section_char = '-'
1575
                list_body_append(
1576
  """<td class="%s" align="left" valign="middle"><a href="portal_selections/foldReport?report_url=%s&form_id=%s&list_selection_name=%s">%s%s %s</a></td>
1577
  """ % (td_css, getattr(stat_context,'domain_url',''), form.id, selection_name, '&nbsp;&nbsp;' * current_section[2], section_char, translate('erp5_content', section_name, default=section_name.decode('utf-8'))))
1578 1579 1580

                if render_format == 'list':

1581 1582 1583 1584 1585 1586 1587 1588 1589 1590
                  if is_summary:
                    current_listboxline.markSummaryLine()
                  # XXX temporary correction (I dont some '' which havent signification)
                  if section_name == '':
                    section_name = None

                  current_listboxline.setSectionDepth( current_section[2]+1 )
                  current_listboxline.setSectionName( section_name )
                  current_listboxline.setSectionFolded( 0 )

1591 1592 1593
              else:
                if section_name != '':
                  section_char = '+'
1594
                list_body_append(
1595
  """<td class="%s" align="left" valign="middle"><a href="portal_selections/unfoldReport?report_url=%s&form_id=%s&list_selection_name=%s">%s%s %s</a></td>
1596
  """ % (td_css, getattr(stat_context,'domain_url',''), form.id, selection_name, '&nbsp;&nbsp;' * current_section[2], section_char, translate('erp5_content', section_name, default=section_name.decode('utf-8'))))
1597

1598 1599
                if render_format == 'list':

1600 1601 1602 1603 1604 1605 1606 1607 1608
                  if is_summary:
                    current_listboxline.markSummaryLine()
                  # XXX temporary correction (I dont some '' which havent signification)
                  if section_name == '':
                    section_name = None

                  current_listboxline.setSectionDepth( current_section[2]+1 )
                  current_listboxline.setSectionName( section_name )
                  current_listboxline.setSectionFolded( 1 )
1609

1610
            if select:
1611 1612 1613 1614 1615 1616
              if o is not None:
                if o.uid in checked_uids:
                  selected = 'checked'
                else:
                  selected = ''
                if is_summary:
1617
                  list_body_append(
1618
    """<td class="%s" width="50" align="center" valign="middle">&nbsp;</td>
1619
    """ % (td_css, ))
1620
                else:
1621
                  list_body_append(
1622
    """<td class="%s" width="50" align="center" valign="middle">
1623
    <input type="checkbox" %s value="%s" id="cb_%s" name="uids:list"/></td>
1624
    """ % (td_css, selected, o.uid , o.uid))
1625
              else:
1626
                list_body_append(
1627
    """<td class="%s" width="50" align="center" valign="middle">&nbsp;</td>
1628
    """ % td_css)
1629

1630
            error_list = []
1631 1632


1633
            if render_format == 'list':
1634 1635 1636 1637 1638 1639 1640 1641 1642 1643
              if o is not None:
                if selected == '':
                  current_listboxline.setObjectUid( o.uid )
                  current_listboxline.checkLine( 0 )
                else:
                  current_listboxline.setObjectUid( o.uid )
                  current_listboxline.checkLine( 1 )

            if o is None:
              # This line is an empty line used by reports without statistics
1644
              list_body_append(('<td class=\"%s\">&nbsp;</td>' % td_css) * len(extended_columns))
1645
            else:
1646
              for cname in extended_columns:
1647
                # add attribute_original_value, because I need to know the type of the attribute
1648
                attribute_original_value = None
1649

1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665
                sql = cname[0] # (sql, title, alias)
                alias = cname[2] # (sql, title, alias)
                if '.' in sql:
                  property_id = '.'.join(sql.split('.')[1:]) # Only take trailing part
                else:
                  property_id = alias
    #            attribute_value = getattr(o, cname_id) # FUTURE WAY OF DOING TGW Brains
                my_field = None
                tales_expr = None
                if form.has_field('%s_%s' % (field.id, alias) ) and not is_summary:
                  my_field_id = '%s_%s' % (field.id, alias)
                  my_field = form.get_field(my_field_id)
                  tales_expr = my_field.tales.get('default', "")
                if tales_expr:
                  #
                  real_o = o
1666
                  if hasattr(aq_base(o),'getObject'): # we have a line of sql result
1667 1668 1669
                    real_o = o.getObject()
                  field_kw = {'cell':real_o}
                  attribute_value = my_field.__of__(real_o).get_value('default',**field_kw)
1670
                  attribute_original_value = attribute_value
1671
                else:
1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688
                  # Prepare stat_column is this is a summary
                  if is_summary:
                    # Use stat method to find value
                    for stat_column in stat_columns:
                      if stat_column[0] == sql:
                        break
                    else:
                      stat_column = None
                  if hasattr(aq_self(o),alias) and (not is_summary or stat_column is None or stat_column[0] == stat_column[1]): # Block acquisition to reduce risks
                    # First take the indexed value
                    attribute_value = getattr(o,alias) # We may need acquisition in case of method call
                    attribute_original_value = attribute_value
                  elif is_summary:
                    attribute_value = getattr(here, stat_column[1])
                    attribute_original_value = attribute_value
                    #LOG('ListBox', 0, 'column = %s, value = %s' % (repr(column), repr(value)))
                    if callable(attribute_value):
1689
                      try:
1690 1691 1692 1693 1694 1695 1696
                        # set report and closed_summary
                        if report_tree == 1 :
                          selection.edit(report=current_section[6])
                          kw['closed_summary'] = 1 - current_section[5]
                          params = dict(kw)
                          selection.edit(params=params)
                        attribute_value=attribute_value(selection=selection)
1697
                        attribute_original_value = attribute_value
1698 1699 1700 1701 1702 1703
                        # reset report and closed_summary
                        if report_tree == 1 :
                          selection.edit(report=None)
                          del kw['closed_summary']
                          params = dict(kw)
                          selection.edit(params=params)
1704
                      except (ConflictError, RuntimeError):
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1705
                        raise
1706 1707 1708 1709 1710 1711 1712 1713 1714
                      except:
                        LOG('ListBox', 0, 'WARNING: Could not call %s with %s: ' % (repr(attribute_value), repr(params)), error=sys.exc_info())
                        pass
                  else:
      #             MUST IMPROVE FOR PERFORMANCE REASON
      #             attribute_value = 'Does not exist'
                    if real_o is None:
                      try:
                        real_o = o.getObject()
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1715
                      except AttributeError:
1716 1717 1718 1719 1720
                        pass
                    if real_o is not None:
                      try:
                        try:
                          attribute_value = getattr(real_o,property_id, None)
1721
                          attribute_original_value = attribute_value
1722 1723 1724 1725 1726
                          #LOG('Look up attribute %s' % cname_id,0,str(attribute_value))
                          if not callable(attribute_value):
                            #LOG('Look up accessor %s' % cname_id,0,'')
                            attribute_value = real_o.getProperty(property_id)
                            attribute_original_value = attribute_value
1727

1728
                            #LOG('Look up accessor %s' % cname_id,0,str(attribute_value))
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1729
                        except AttributeError:
1730 1731 1732
                          attribute_value = getattr(real_o,property_id)
                          attribute_original_value = attribute_value
                          #LOG('Fallback to attribute %s' % cname_id,0,str(attribute_value))
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1733
                      except AttributeError:
1734 1735 1736 1737
                        attribute_value = 'Can not evaluate attribute: %s' % sql
                        attribute_original_value = None
                    else:
                      attribute_value = 'Object does not exist'
1738
                      attribute_original_value = None
1739
                if callable(attribute_value):
1740
                  try:
1741 1742 1743 1744 1745 1746
                    try:
                      attribute_value = attribute_value(brain = o, selection = selection)
                      attribute_original_value = attribute_value
                    except TypeError:
                      attribute_value = attribute_value()
                      attribute_original_value = attribute_value
1747
                  except (ConflictError, RuntimeError):
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1748
                    raise
1749 1750 1751 1752 1753
                  except:
                    LOG('ListBox', 0, 'Could not evaluate', error=sys.exc_info())
                    attribute_value = "Could not evaluate"
                    attribute_original_value = None
                #LOG('ListBox', 0, 'o = %s' % repr(dir(o)))
1754
                if isinstance(attribute_value, float) :
1755 1756 1757 1758 1759
                  attribute_original_value = attribute_value
                  if sql in editable_column_ids and form.has_field('%s_%s' % (field.id, alias) ):
                    # Do not truncate if editable
                    pass
                  else:
1760
                    #attribute_original_value = attribute_value
1761
                    attribute_value = float("%.2f" % attribute_value)
1762
                  td_align = "right"
1763
                elif isinstance(attribute_value, (int, long)):
1764
                  attribute_original_value = attribute_value
1765 1766 1767
                  td_align = "right"
                else:
                  td_align = "left"
1768 1769 1770
                  if attribute_value is None:
                    attribute_original_value = None
                    attribute_value = ''
1771
                  elif isinstance(attribute_value, str) :
1772
                    attribute_original_value = attribute_value
1773
                    attribute_value = unicode(str(attribute_value), 'utf-8')
1774 1775 1776 1777
                if sql in editable_column_ids and form.has_field('%s_%s' % (field.id, alias)) and not is_summary:
                  key = my_field.id + '_%s' % o.uid
                  if field_errors.has_key(key):
                    error_css = 'Error'
1778
                    error_message = "<br/>%s" % translate('erp5_ui', field_errors[key].error_text)
1779 1780 1781 1782 1783 1784 1785 1786 1787
                    # Display previous value (in case of error
                    error_list.append(field_errors.get(key))
                    display_value = REQUEST.get('field_%s' % key, attribute_value)
                  else:
                    error_css = ''
                    error_message = ''
                    display_value = attribute_value # XXX Make sure this is ok
                  if type(display_value) == type(u''):
                    display_value = display_value.encode('utf-8')
1788
                  if my_field.meta_type not in ('DateTimeField', 'ProxyField',):
1789 1790 1791 1792 1793 1794 1795 1796 1797
                    cell_body = my_field.render(value = display_value, REQUEST = o, key = key)
                                                                # We use REQUEST which is not so good here
                                                                # This prevents from using standard display process
                  else: # Some fields prefer a None value to a ''
                    cell_body = my_field.render(value = attribute_original_value, REQUEST = o.asContext(REQUEST=REQUEST, form=REQUEST.form), key = key)
                  # It is safer to convert cell_body to an unicode string, because
                  # it might be utf-8.
                  if type(cell_body) == type(''):
                    cell_body = unicode(cell_body, 'utf-8')
1798 1799
                  if type(attribute_value) == type(u''):
                    attribute_value = cgi.escape(attribute_value)
1800
                  #LOG('ListBox', 0, 'cell_body = %r, error_message = %r' % (cell_body, error_message))
1801
                  list_body_append('<td class=\"%s%s\">%s%s</td>' % (td_css, error_css, cell_body, error_message))
1802 1803


1804 1805 1806 1807 1808 1809 1810
                  # Add item to list_result_item for list render format
  #                if render_format == 'list':
  #                  column_value = my_field._get_default(my_field.generate_field_key(), attribute_original_value, o)
  #                  if type(column_value) is type(u''):
  #                    #column_value = unicode(column_value, 'utf-8')
  #                    column_value = column_value.encode('utf-8')
  #                  current_listboxline.addColumn(property_id , column_value)
1811
                  if render_format == 'list':
1812 1813 1814 1815 1816 1817 1818 1819 1820 1821
                    subfield = getattr(form,
                                       "%s_%s" % (field.id, alias),
                                       None)
                    # If we have a listfield, then display the same
                    # value that would be displayed in html
                    if subfield is not None and subfield.has_value("items") :
                      field_kw = {'cell': real_o}
                      items = subfield.get_value('items', **field_kw)
                      for display, value in items:
                        if value == attribute_original_value :
1822
                          attribute_original_value = display
1823
                          break
1824 1825 1826 1827
                    # Make sure that attribute value is UTF-8
                    attribute_value_tmp  = attribute_original_value
                    if type(attribute_value_tmp) == type(u''):
                      attribute_value_tmp = attribute_original_value.encode('utf-8')
1828

1829 1830
                    # XXX this is horrible, but it would be better without those &nbsp; ....
                    if type(attribute_value_tmp) == type(''):
1831 1832
                      if '&nbsp;' in attribute_value_tmp:
                        attribute_value_tmp = attribute_value_tmp.replace('&nbsp;', ' ')
1833

1834
                    current_listboxline.addColumn( cname[0] , attribute_value_tmp)
1835

1836
                else:
1837 1838 1839 1840 1841
                  #########################################################################
                  # Link generation
                  #########################################################################
                  object_url = None
                  # Try to get a link
1842
                  # Check if url_columns defines a method
1843 1844 1845 1846 1847 1848 1849 1850 1851 1852
                  # to retrieve the URL.
                  url_method = None
                  for column in url_columns:
                    if sql == column[0]:
                      url_method = getattr(o, column[1], '')
                      break
                  if url_method:
                    # Call the requested method
                    try:
                      object_url = url_method(brain=o, selection=selection)
1853
                    except (ConflictError, RuntimeError):
1854 1855
                      raise
                    except:
1856
                      LOG('ListBox', 0,
1857 1858 1859 1860 1861 1862 1863
                          'Could not evaluate url_method %s' % \
                              column[1], error=sys.exc_info())
                      pass
                  elif url_method is None:
                    # Check if this object provides a specific URL method
                    url_method = getattr(o, 'getListItemUrl', None)
                    if url_method is None:
1864
                      try:
1865 1866 1867 1868 1869 1870 1871
                        object_url = o.absolute_url() + \
                          '/view?selection_index=%s&selection_name=%s&reset=1' % (i, selection_name)
                      except AttributeError:
                        pass
                    else:
                      try:
                        object_url = url_method(alias, i, selection_name)
1872
                      except (ConflictError, RuntimeError):
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1873
                        raise
1874
                      except:
1875 1876
                        pass
                  # Generate appropriate HTML
1877
                  if type(attribute_value) == type(u''):
1878
                    attribute_value = cgi.escape(attribute_value)
1879
                  if object_url is None:
1880 1881
                    list_body_append(
                      "<td class=\"%s\" align=\"%s\">%s</td>" % \
1882 1883
                                  (td_css, td_align, attribute_value))
                  else:
1884 1885
                    list_body_append(
                      "<td class=\"%s\" align=\"%s\"><a href=\"%s\">%s</a></td>" %
1886
                        (td_css, td_align, object_url, attribute_value))
1887 1888

                  if render_format == 'list':
1889 1890 1891 1892
                    # Make sure that attribute value is UTF-8
                    attribute_value_tmp  = attribute_original_value
                    if type(attribute_value_tmp) == type(u''):
                      attribute_value_tmp = attribute_original_value.encode('utf-8')
1893

1894 1895
                    # XXX this is horrible, but it would be better without those &nbsp; ....
                    if type(attribute_value_tmp) == type(''):
1896 1897
                      if '&nbsp;' in attribute_value_tmp:
                        attribute_value_tmp = attribute_value_tmp.replace('&nbsp;', ' ')
1898

1899
                    current_listboxline.addColumn( cname[0] , attribute_value_tmp)
1900

1901
            list_body_append('</tr>')
1902

1903 1904

            if render_format == 'list':
1905 1906
              listboxline_list.append(current_listboxline)

1907
        ###############################################################
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1908
        #
1909
        # Build statistics
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1910
        #
1911
        ###############################################################
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1912

1913
        # Call the stat method
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1914
        if show_stat:
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1915

1916 1917 1918 1919
          if render_format == 'list':
            # Create a ListBoxLine object
            current_listboxline = ListBoxLine()
            current_listboxline.markStatLine()
1920

1921 1922
          kw['select_expression'] = select_expression
          selection.edit( params = kw )
1923
          count_results = selection(method = stat_method,
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1924
                          context=here, REQUEST=REQUEST)
1925
          list_body_append('<tr id="listbox_stat_line">')
1926 1927


1928
          if report_tree:
1929
            list_body_append('<td class="Data">&nbsp;</td>')
1930
          if select:
1931
            list_body_append('<td class="Data">&nbsp;</td>')
1932 1933
          for n in range((len(extended_columns))):
            try:
1934 1935 1936 1937 1938 1939 1940 1941 1942 1943
              sql = extended_columns[n][0]
              for column in stat_columns:
                if column[0] == sql:
                  break
              else:
                column = None
              #LOG('ListBox', 0, 'n = %s, extended_columns = %s, stat_columns = %s, column = %s' % (repr(n), repr(extended_columns), repr(stat_columns), repr(column)))
              if column is not None:
                if column[0] == column[1]:
                  alias = extended_columns[n][2]
1944 1945
                  if len(count_results):
                    value = getattr(count_results[0],alias,'')
1946 1947 1948 1949 1950
                else:
                  value = getattr(here, column[1])
                  #LOG('ListBox', 0, 'column = %s, value = %s' % (repr(column), repr(value)))
                  if callable(value):
                    try:
1951
                      #params = dict(kw)
1952
                      #params['operator'] = stats[n]
1953 1954
                      #value=value(**params)
                      value=value(selection=selection)
1955
                    except (ConflictError, RuntimeError):
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1956
                      raise
1957 1958 1959 1960
                    except:
                      LOG('ListBox', 0, 'WARNING: Could not call %s with %s: ' % (repr(value), repr(params)), error=sys.exc_info())
                      pass
                if type(value) is type(1.0):
1961
                  list_body_append('<td class="Data" align="right">%.2f</td>' % value)
Jérome Perrin's avatar
Jérome Perrin committed
1962
                elif type(value) is type(1) :
1963
                  list_body_append('<td class="Data" align="right">%s</td>' % value)
1964
                else:
1965
                  list_body_append('<td class="Data">' + str(value) + '</td>')
1966 1967


1968
                if render_format == 'list':
1969
                  # Make sure that attribute value is UTF-8
1970
                  value_tmp  = value
1971
                  if type(value) == type(u''):
1972 1973 1974 1975
                    value_tmp = value.encode('utf-8')

                  # XXX this is horrible, but it would be better without those &nbsp; ....
                  if type(value_tmp) == type(''):
1976
                    if '&nbsp;' in value_tmp:
1977
                      value_tmp = value_tmp.replace('&nbsp;', ' ')
1978

1979
                  current_listboxline.addColumn( column[0] , value_tmp )
1980

1981
              else:
1982
                list_body_append('<td class="Data">&nbsp;</td>')
1983 1984
                if render_format == 'list':
                  current_listboxline.addColumn( extended_columns[n][0], None)
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1985
            except KeyError:
1986
              list_body_append('<td class="Data">&nbsp;</td>')
Romain Courteaud's avatar
Romain Courteaud committed
1987
              #if render_format == 'list': current_listboxline.addColumn( column[1] , None)
1988
              if render_format == 'list': current_listboxline.addColumn( extended_columns[n][0] , None)
1989
          list_body_append('</tr>')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1990

1991
          if render_format == 'list':
1992 1993
            listboxline_list.append(current_listboxline)

1994 1995
        #LOG('ListBox', 0, 'header = %r, selection_list = %r, list_header = %r, list_search = %r, list_body = %r, footer = %r' % (header, selection_line, list_header, list_search, list_body, footer))
        #LOG('ListBox', 0, 'header = %r, selection_list = %r, list_header = %r, list_search = %r, footer = %r' % (header, selection_line, list_header, list_search, footer))
1996
        list_body = ''.join(list_body_list)
1997
        list_html = header + selection_line + list_header + list_search + list_body + footer
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1998

1999

2000
        if render_format == 'list':
2001
          #listboxline_list.append(current_listboxline)
2002
          #LOG('ListBox', 0, 'listboxline_list: %s' % str(listboxline_list) )
2003 2004

          return listboxline_list
Yoshinori Okuji's avatar
Yoshinori Okuji committed
2005

Jean-Paul Smets's avatar
Jean-Paul Smets committed
2006 2007 2008
        #Create DomainTree Selector and DomainTree box
        if domain_tree:
          select_tree_options = ''
2009
          default_selected = ''
Jean-Paul Smets's avatar
Jean-Paul Smets committed
2010 2011
          for c in domain_root_list:
            if c[0] == selection_domain_path:
2012
              select_tree_options += """<option selected value="%s">%s</option>\n""" % (c[0], translate('erp5_ui', c[1]))
Jean-Paul Smets's avatar
Jean-Paul Smets committed
2013
            else:
2014
              select_tree_options += """<option value="%s">%s</option>\n""" % (c[0], translate('erp5_ui', c[1]))
2015 2016
              if default_selected == '':
                default_selected = c[0] #the first is selected
Jean-Paul Smets's avatar
Jean-Paul Smets committed
2017 2018 2019 2020 2021
          select_tree_header = """<select name="domain_root_url"
onChange="submitAction(this.form,'%s/portal_selections/setDomainRoot')">
        %s</select>""" % (here.getUrl(),select_tree_options)

          try:
2022 2023
              if selection_domain_path == ('portal_categories',):
                selection_domain_path = default_selected
2024 2025 2026
              select_tree_body = makeTreeBody(form = form, domain_path = selection_domain_path,
                                              unfolded_list = selection_domain_current,
                                              selection_name = selection_name)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
2027
          except KeyError:
2028
              select_tree_body = ''
Jean-Paul Smets's avatar
Jean-Paul Smets committed
2029 2030
          select_tree_html = """<!-- Select Tree -->
%s
Yoshinori Okuji's avatar
Yoshinori Okuji committed
2031
<table id="%s_domain_tree_table" cellpadding="0" border="0">
Jean-Paul Smets's avatar
Jean-Paul Smets committed
2032 2033
%s
</table>
Yoshinori Okuji's avatar
Yoshinori Okuji committed
2034
"""  % (select_tree_header, field.id, select_tree_body )
Jean-Paul Smets's avatar
Jean-Paul Smets committed
2035 2036

          return """<!-- Table Wrapping for Select Tree -->
Yoshinori Okuji's avatar
Yoshinori Okuji committed
2037 2038
<table border="0" cellpadding="0" cellspacing="0" width="100%%">
<tr><td valign="top">
Jean-Paul Smets's avatar
Jean-Paul Smets committed
2039 2040 2041 2042 2043
%s
</td><td valign="top">
%s
<!-- End of Table Wrapping for Select Tree -->
</td></tr>
Yoshinori Okuji's avatar
Yoshinori Okuji committed
2044
</table>""" % (select_tree_html, list_html)
2045 2046 2047
        #render_end = DateTime()
        #result = (render_end-render_start) * 86400
        #LOG('Listbox render end call', 0, result)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063
        return list_html

ListBoxWidgetInstance = ListBoxWidget()

class ListBoxValidator(Validator.Validator):
    property_names = Validator.Validator.property_names

    def validate(self, field, key, REQUEST):
        form = field.aq_parent
        # We need to know where we get the getter from
        # This is coppied from ERP5 Form
        here = getattr(form, 'aq_parent', REQUEST)
        columns = field.get_value('columns')
        editable_columns = field.get_value('editable_columns')
        column_ids = map(lambda x: x[0], columns)
        editable_column_ids = map(lambda x: x[0], editable_columns)
2064
        selection_name = field.get_value('selection_name')
2065
        #LOG('ListBoxValidator', 0, 'field = %s, selection_name = %s' % (repr(field), repr(selection_name)))
2066
        selection = here.portal_selections.getSelectionFor(selection_name, REQUEST=REQUEST)
2067
        params = selection.getParams()
2068 2069
        portal_url = getToolByName(here, 'portal_url')
        portal = portal_url.getPortalObject()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
2070

2071

Jean-Paul Smets's avatar
Jean-Paul Smets committed
2072
        result = {}
2073
        error_result = {}
Jean-Paul Smets's avatar
Jean-Paul Smets committed
2074
        listbox_uids = REQUEST.get('%s_uid' % field.id, [])
2075
        #LOG('ListBox.validate: REQUEST',0,REQUEST)
Sebastien Robin's avatar
Sebastien Robin committed
2076
        errors = []
2077 2078 2079 2080 2081 2082 2083 2084 2085 2086
        object_list = []
        # We have two things to do in the case of temp objects,
        # the first thing is to create a list with new temp objects
        # then try to validate some data, and then create again
        # the list with a listbox as parameter. Like this we
        # can use tales expression
        for uid in listbox_uids:
          if str(uid).find('new') == 0:
            list_method = field.get_value('list_method')
            list_method = getattr(here, list_method.method_name)
2087
            #LOG('ListBoxValidator', 0, 'call %s' % repr(list_method))
2088
            object_list = list_method(REQUEST=REQUEST, **params)
2089 2090 2091 2092 2093 2094 2095 2096
            break
        listbox = {}
        for uid in listbox_uids:
          if str(uid).find('new') == 0:
            o = None
            for object in object_list:
              if object.getUid()==uid:
                o = object
2097 2098 2099 2100
            if o is None:
              # First case: dialog input to create new objects
              o = newTempBase(portal, uid[4:]) # Arghhh - XXX acquisition problem - use portal root
              o.uid = uid
2101
            listbox[uid[4:]] = {}
2102 2103
            # We first try to set a listbox corresponding to all things
            # we can validate, so that we can use the same list
2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128
            # as the one used for displaying the listbox
            for sql in editable_column_ids:
              alias = '_'.join(sql.split('.'))
              if '.' in sql:
                property_id = '.'.join(sql.split('.')[1:]) # Only take trailing part
              else:
                property_id = alias
              my_field_id = '%s_%s' % (field.id, alias)
              if form.has_field( my_field_id ):
                my_field = form.get_field(my_field_id)
                key = 'field_' + my_field.id + '_%s' % o.uid
                error_result_key = my_field.id + '_%s' % o.uid
                REQUEST.cell = o
                try:
                  value = my_field.validator.validate(my_field, key, REQUEST) # We need cell
                  # Here we set the property
                  listbox[uid[4:]][sql] = value
                except ValidationError, err: # XXXX import missing
                  pass
        # Here we generate again the object_list with listbox the listbox we
        # have just created
        if len(listbox)>0:
          list_method = field.get_value('list_method')
          list_method = getattr(here, list_method.method_name)
          REQUEST.set('listbox',listbox)
2129
          object_list = list_method(REQUEST=REQUEST,**params)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
2130
        for uid in listbox_uids:
2131 2132
          if str(uid).find('new') == 0:
            # First case: dialog input to create new objects
2133 2134 2135 2136 2137 2138
            #o = newTempBase(here, uid[4:]) # Arghhh - XXX acquisition problem - use portal root
            #o.uid = uid
            o = None
            for object in object_list:
              if object.getUid()==uid:
                o = object
2139 2140 2141 2142
            if o is None:
              # First case: dialog input to create new objects
              o = newTempBase(portal, uid[4:]) # Arghhh - XXX acquisition problem - use portal root
              o.uid = uid
2143
            result[uid[4:]] = {}
2144 2145 2146 2147 2148 2149 2150
            for sql in editable_column_ids:
              alias = '_'.join(sql.split('.'))
              if '.' in sql:
                property_id = '.'.join(sql.split('.')[1:]) # Only take trailing part
              else:
                property_id = alias
              my_field_id = '%s_%s' % (field.id, alias)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
2151 2152
              if form.has_field( my_field_id ):
                my_field = form.get_field(my_field_id)
2153 2154
                REQUEST.cell = o
                if my_field.get_value('editable',REQUEST=REQUEST) and field.need_validate(REQUEST):
2155 2156 2157 2158 2159
                  key = 'field_' + my_field.id + '_%s' % o.uid
                  error_result_key = my_field.id + '_%s' % o.uid
                  try:
                    value = my_field.validator.validate(my_field, key, REQUEST) # We need cell
                    result[uid[4:]][sql] = value
2160
                  except ValidationError, err:
2161 2162 2163
                    #LOG("ListBox ValidationError",0,str(err))
                    err.field_id = error_result_key
                    errors.append(err)
2164 2165
          else:
            # Second case: modification of existing objects
2166 2167
            #try:
            if 1: #try:
2168 2169
              # We must try this
              # because sometimes, we can be provided bad uids
2170 2171
              try :
                o = here.portal_catalog.getObject(uid)
2172
              except (KeyError, NotFound, ValueError):
2173
                o = None
2174 2175 2176 2177 2178 2179 2180 2181
              if o is None:
                # It is possible that this object is not catalogged yet. So
                # the object must be obtained from ZODB.
                if not object_list:
                  list_method = field.get_value('list_method')
                  list_method = getattr(here, list_method.method_name)
                  object_list = list_method(REQUEST=REQUEST,**params)
                for object in object_list:
2182 2183 2184 2185 2186 2187 2188 2189
                  try:
                    if object.getUid() == int(uid):
                      o = object
                      break
                  except ValueError:
                    if str(object.getUid()) == uid:
                      o = object
                      break
2190 2191 2192 2193 2194 2195 2196 2197 2198
              for sql in editable_column_ids:
                alias = '_'.join(sql.split('.'))
                if '.' in sql:
                  property_id = '.'.join(sql.split('.')[1:]) # Only take trailing part
                else:
                  property_id = alias
                my_field_id = '%s_%s' % (field.id, alias)
                if form.has_field( my_field_id ):
                  my_field = form.get_field(my_field_id)
2199 2200
                  REQUEST.cell = o # We need cell
                  if my_field.get_value('editable',REQUEST=REQUEST) and field.need_validate(REQUEST):
2201 2202 2203
                    tales_expr = my_field.tales.get('default', "")
                    key = 'field_' + my_field.id + '_%s' % o.uid
                    error_result_key = my_field.id + '_%s' % o.uid
2204
                    try:
2205 2206 2207
                      value = my_field.validator.validate(my_field, key, REQUEST) # We need cell
                      error_result[error_result_key] = value
                      if not result.has_key(o.getUrl()):
2208
                        result[o.getUrl()] = {}
2209
                      result[o.getUrl()][sql] = value
2210
                    except ValidationError, err:
2211 2212 2213
                      #LOG("ListBox ValidationError",0,str(err))
                      err.field_id = error_result_key
                      errors.append(err)
2214 2215
            #except:
            else:
2216
              LOG("ListBox WARNING",0,"Object uid %s could not be validated" % uid)
2217
        if len(errors) > 0:
2218 2219
            LOG("ListBox FormValidationError",0,str(error_result))
            LOG("ListBox FormValidationError",0,str(errors))
2220
            raise FormValidationError(errors, error_result)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
2221 2222 2223 2224 2225 2226 2227 2228 2229 2230
        return result

ListBoxValidatorInstance = ListBoxValidator()

class ListBox(ZMIField):
    meta_type = "ListBox"

    widget = ListBoxWidgetInstance
    validator = ListBoxValidatorInstance

2231
    security = ClassSecurityInfo()
Yoshinori Okuji's avatar
Yoshinori Okuji committed
2232

2233 2234
    security.declareProtected('Access contents information', 'get_value')
    def get_value(self, id, **kw):
2235 2236 2237 2238 2239
      if (id == 'default') and \
         (kw.get('render_format') in ('list', )):
          return self.widget.render(self, self.generate_field_key(), None, 
                                    kw.get('REQUEST'), 
                                    render_format=kw.get('render_format'))
2240
      else:
Yoshinori Okuji's avatar
Yoshinori Okuji committed
2241 2242 2243 2244 2245 2246 2247
        # Try an ERP5-style accessor, if available.
        method_id = 'get' + convertToUpperCase(id)
        method = getattr(self.widget, method_id, None)
        if method is not None:
          return method(**kw)
        else:
          return ZMIField.get_value(self, id, **kw)
Yoshinori Okuji's avatar
Yoshinori Okuji committed
2248

2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260


class ListBoxLine:
  meta_type = "ListBoxLine"
  security = ClassSecurityInfo()
  #security.declareObjectPublic()

  def __init__(self):
    """
      Initialize the line and set the default values
      Selected columns must be defined in parameter of listbox.render...
    """
2261

2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279
    self.is_title_line = 0
    self.is_data_line = 1
    self.is_stat_line = 0
    self.is_summary_line = 0

    self.is_section_folded = 1

    self.config_dict = {
      'is_checked' : 0,
      'uid' : None,
      'section_name' : None,
      'section_depth' : 0,
      'content_mode' : 'DataLine'
    }
    self.config_display_list = []

    self.column_dict = {}
    self.column_id_list = []
2280

2281 2282
  security.declarePublic('__getitem__')
  def __getitem__(self, column_id):
2283
    return self.getColumnProperty(column_id)
2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299

  #security.declarePublic('View')
  def setConfigProperty(self, config_id, config_value):
    self.config_dict[config_id] = config_value

  #security.declarePublic('View')
  def getConfigProperty(self, config_id):
    return self.config_dict[config_id]

  #security.declarePublic('View')
  def setListboxLineContentMode(self, content_mode):
    """
      Toogle the content type of the line
      content_mode can be 'TitleLine' 'StatLine' 'DataLine'
      Default value is 'DataLine'
    """
2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310
    content_mode_dict = {
      'TitleLine':(1,0,0,0),
      'DataLine':(0,1,0,0),
      'StatLine':(0,0,1,0),
      'SummaryLine':(0,0,0,1)
    }
    self.is_title_line,\
    self.is_data_line,\
    self.is_stat_line,\
    self.is_summary_line = content_mode_dict[content_mode]

2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332
    self.setConfigProperty('content_mode',content_mode)

  #security.declarePublic('View')
  def markTitleLine(self):
    """
      Set content of the line to 'TitleLine'
    """
    self.setListboxLineContentMode('TitleLine')

  security.declarePublic('isTitleLine')
  def isTitleLine(self):
    """
      Returns 1 is this line contains no data but only title of columns
    """
    return self.is_title_line

  #security.declarePublic('View')
  def markStatLine(self):
    """
      Set content of the line to 'StatLine'
    """
    self.setListboxLineContentMode('StatLine')
2333

2334 2335
  security.declarePublic('isStatLine')
  def isStatLine(self):
2336 2337 2338 2339
    """
      Returns 1 is this line contains no data but only stats
    """
    return self.is_stat_line
2340

2341 2342 2343 2344 2345 2346
  #security.declarePublic('View')
  def markDataLine(self):
    """
      Set content of the line to 'DataLine'
    """
    self.setListboxLineContentMode('DataLine')
2347

2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382
  security.declarePublic('isDataLine')
  def isDataLine(self):
    """
      Returns 1 is this line contains data
    """
    return self.is_data_line

  #security.declarePublic('View')
  def markSummaryLine(self):
    """
      Set content of the line to 'SummaryLine'
    """
    self.setListboxLineContentMode('SummaryLine')

  security.declarePublic('isSummaryLine')
  def isSummaryLine(self):
    """
      Returns 1 is this line is a summary line
    """
    return self.is_summary_line

  #security.declarePublic('View')
  def checkLine(self, is_checked):
    """
      Set line to checked if is_checked=1
      Default value is 0
    """
    self.setConfigProperty('is_checked',is_checked)

  security.declarePublic('isLineChecked')
  def isLineChecked(self):
    """
      Returns 1 is this line is checked
    """
    return self.getConfigProperty('is_checked')
2383

2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397
  #security.declarePublic('View')
  def setObjectUid(self, object_uid):
    """
      Define the uid of the object
      Default value is None
    """
    self.setConfigProperty('uid',object_uid)

  security.declarePublic('getObjectUid')
  def getObjectUid(self):
    """
      Get the uid of the object related to the line
    """
    return self.getConfigProperty('uid')
2398

2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421
  #security.declarePublic('View')
  def setSectionName(self, section_name):
    """
      Set the section name of this line
      Default value is None
    """
    self.setConfigProperty('section_name',section_name)

  security.declarePublic('getSectionName')
  def getSectionName(self):
    """
      Returns the section name of this line
      Default value is None
    """
    return self.getConfigProperty('section_name')

  #security.declarePublic('View')
  def setSectionDepth(self, depth):
    """
      Set the section depth of this line
      default value is 0 and means no depth
    """
    self.setConfigProperty('section_depth',depth)
2422

2423 2424 2425 2426 2427 2428 2429
  security.declarePublic('getSectionDepth')
  def getSectionDepth(self):
    """
      Returns the section depth of this line
      0 means no depth
    """
    return self.getConfigProperty('section_depth')
2430

2431 2432 2433 2434 2435 2436
  #security.declarePublic('View')
  def setSectionFolded(self, is_section_folded):
    """
      Set the section mode of this line to 'Folded' if is_section_folded=1
    """
    self.is_section_folded = is_section_folded
2437 2438


2439 2440 2441 2442 2443
  security.declarePublic('isSectionFolded')
  def isSectionFolded(self):
    """
      Returns 1 if section is in 'Folded' Mode
    """
2444 2445
    return self.is_section_folded

2446 2447 2448
  #security.declarePublic('View')
  def addColumn(self, column_id, column_value):
    """
2449
      Add a new column
2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464
    """
    self.column_dict[column_id] = column_value
    self.column_id_list.append(column_id)

  security.declarePublic('getColumnProperty')
  def getColumnProperty(self, column_id):
    """
      Returns the property of a column
    """
    return self.column_dict[column_id]


  security.declarePublic('getColumnPropertyList')
  def getColumnPropertyList(self, column_id_list = None):
    """
2465
      Returns a list of the property
2466 2467
      column_id_list selects the column_id returned
    """
2468

2469 2470
    if column_id_list == None:
      column_id_list = self.column_id_list
2471

2472 2473 2474 2475
    if self.isTitleLine():
      config_column = [None] * len(self.config_display_list)
    else:
      config_column = [self.config_dict[column_id] for column_id in self.config_display_list]
2476 2477


2478 2479 2480 2481 2482 2483 2484 2485
    return config_column + [self.column_dict[column_id] for column_id in column_id_list]

  security.declarePublic('getColumnItemList')
  def getColumnItemList(self, column_id_list = None ):
    """
      Returns a list of property tuple
      column_id_list selects the column_id returned
    """
2486

2487 2488
    if column_id_list == None:
      column_id_list = self.column_id_list
2489

2490 2491 2492 2493 2494 2495 2496
    """
    if self.isTitleLine():
      config_column = [None] * len(self.config_display_list)
    else:
      config_column = [(config_id, self.config_dict[column_id]) for config_id in self.config_display_list]
    """
    config_column = [(config_id, self.config_dict[config_id]) for config_id in self.config_display_list]
2497

2498
    return config_column + [(column_id , self.column_dict[column_id]) for column_id in column_id_list]
2499

2500 2501 2502 2503 2504 2505 2506 2507
  security.declarePublic('setListboxLineDisplayListMode')
  def setListboxLineDisplayListMode(self, display_list):
    """
      Set the config columns displayable
      display_list can content the key of self.config_dict
      Default value of display_list is []
    """
    self.config_display_list = display_list
2508

2509 2510 2511
InitializeClass(ListBoxLine)
allow_class(ListBoxLine)

Jean-Paul Smets's avatar
Jean-Paul Smets committed
2512 2513 2514 2515 2516 2517
# Psyco
import psyco
psyco.bind(ListBoxWidget.render)
psyco.bind(ListBoxValidator.validate)