SelectionTool.py 68.2 KB
Newer Older
1
# -*- coding: utf-8 -*-
Jean-Paul Smets's avatar
Jean-Paul Smets committed
2 3
##############################################################################
#
4
# Copyright (c) 2002,2007 Nexedi SARL and Contributors. All Rights Reserved.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
5
#                    Jean-Paul Smets-Solanes <jp@nexedi.com>
Jean-Paul Smets's avatar
Jean-Paul Smets committed
6 7
#
# WARNING: This program as such is intended to be used by professional
8
# programmers who take the whole responsibility of assessing all potential
Jean-Paul Smets's avatar
Jean-Paul Smets committed
9 10
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
11
# guarantees and support are strongly adviced to contract a Free Software
Jean-Paul Smets's avatar
Jean-Paul Smets committed
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
# 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.
#
##############################################################################

30 31
"""
  ERP5 portal_selection tool.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
32 33 34 35 36 37
"""

from OFS.SimpleItem import SimpleItem
from Products.CMFCore.utils import UniqueObject
from Globals import InitializeClass, DTMLFile, PersistentMapping, get_request
from AccessControl import ClassSecurityInfo
38
from Products.ERP5Type.Tool.BaseTool import BaseTool
Jean-Paul Smets's avatar
Jean-Paul Smets committed
39 40
from Products.ERP5Type import Permissions as ERP5Permissions
from Products.ERP5Form import _dtmldir
41
from Selection import Selection, DomainSelection
42
from ZPublisher.HTTPRequest import FileUpload
Sebastien Robin's avatar
Sebastien Robin committed
43
import md5
44
import string, re
45
from urlparse import urlsplit, urlunsplit
46 47
from zLOG import LOG, INFO
from Acquisition import aq_base
48
from Products.ERP5Type.Message import translateString
49
import warnings
50

51

52 53
_MARKER = []

Jean-Paul Smets's avatar
Jean-Paul Smets committed
54 55 56
class SelectionError( Exception ):
    pass

57
class SelectionTool( BaseTool, UniqueObject, SimpleItem ):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
58 59 60 61 62 63 64 65
    """
      The SelectionTool object is the place holder for all
      methods and algorithms related to persistent selections
      in ERP5.
    """

    id              = 'portal_selections'
    meta_type       = 'ERP5 Selections'
66
    portal_type     = 'Selection Tool'
Jean-Paul Smets's avatar
Jean-Paul Smets committed
67 68 69 70 71 72 73
    security = ClassSecurityInfo()

    #
    #   ZMI methods
    #
    manage_options = ( ( { 'label'      : 'Overview'
                         , 'action'     : 'manage_overview'
74 75
                         },
                         { 'label'      : 'View Selections'
76
                         , 'action'     : 'manage_viewSelections'
77 78 79
                         },
                         { 'label'      : 'Configure'
                         , 'action'     : 'manage_configure'
80
                         } ))
Jean-Paul Smets's avatar
Jean-Paul Smets committed
81 82 83

    security.declareProtected( ERP5Permissions.ManagePortal
                             , 'manage_overview' )
84
    manage_overview = DTMLFile( 'explainSelectionTool', _dtmldir )
Jean-Paul Smets's avatar
Jean-Paul Smets committed
85

86
    security.declareProtected( ERP5Permissions.ManagePortal
87 88
                             , 'manage_viewSelections' )
    manage_viewSelections = DTMLFile( 'SelectionTool_manageViewSelections', _dtmldir )
89

90 91 92 93
    security.declareProtected( ERP5Permissions.ManagePortal
                             , 'manage_configure' )
    manage_configure = DTMLFile( 'SelectionTool_configure', _dtmldir )

94 95 96 97 98 99 100 101 102 103 104
    security.declareProtected( ERP5Permissions.ManagePortal
                             , 'manage_deleteSelection' )
    def manage_deleteSelection(self, selection_name, REQUEST=None):
      """
        Relete a specified selection
      """
      self._deleteSelectionFromContainer(selection_name)
      if REQUEST is not None:
        return REQUEST.RESPONSE.redirect('%s/%s' %
                (self.absolute_url(), 'manage_viewSelections'))

105 106 107 108 109 110 111 112 113 114 115
    security.declareProtected( ERP5Permissions.ManagePortal
                             , 'manage_deleteGlobalSelection' )
    def manage_deleteGlobalSelection(self, selection_name, REQUEST=None):
      """
        Relete a specified selection
      """
      self._deleteGlobalSelectionFromContainer(selection_name)
      if REQUEST is not None:
        return REQUEST.RESPONSE.redirect('%s/%s' %
                (self.absolute_url(), 'manage_viewSelections'))

116
    # storages of SelectionTool
117 118 119 120 121 122 123 124 125 126 127
    security.declareProtected(ERP5Permissions.ManagePortal
                              , 'getStorageItemList')
    def getStorageItemList(self):
      """Return the list of available storages
      """
      #storage_item_list = [('Persistent Mapping', 'selection_data',)]
      #list of tuple may fail dtml code: zope/documenttemplate/dt_in.py +578
      storage_item_list = [['Persistent Mapping', 'selection_data']]
      memcached_plugin_list = self.portal_memcached.contentValues(portal_type='Memcached Plugin', sort_on='int_index')
      storage_item_list.extend([['/'.join((mp.getParentValue().getTitle(), mp.getTitle(),)), mp.getRelativeUrl()] for mp in memcached_plugin_list])
      return storage_item_list
128 129 130 131 132 133

    security.declareProtected( ERP5Permissions.ManagePortal, 'setStorage')
    def setStorage(self, value, RESPONSE=None):
      """
        Set the storage of Selection Tool.
      """
134
      if value in [item[1] for item in self.getStorageItemList()]:
135 136 137 138 139 140
        self.storage = value
      else:
        raise ValueError, 'Given storage type (%s) is now supported.' % (value,)
      if RESPONSE is not None:
        RESPONSE.redirect('%s/manage_configure' % (self.absolute_url()))

141
    security.declareProtected( ERP5Permissions.ManagePortal, 'getStorage')
142
    def getStorage(self, default='selection_data'):
143 144
      """return the selected storage
      """
145
      storage = getattr(aq_base(self), 'storage', default)
146
      if storage is not default:
147 148 149 150 151 152
        #Backward compatibility
        if storage == 'Persistent Mapping':
          storage = 'selection_data'
        elif storage == 'Memcached Tool':
          memcached_plugin_list = self.portal_memcached.contentValues(portal_type='Memcached Plugin', sort_on='int_index')
          storage = memcached_plugin_list[0].getRelativeUrl()
153 154 155
      return storage

    def isMemcachedUsed(self):
156
      return 'portal_memcached' in self.getStorage()
157

Vincent Pelletier's avatar
Vincent Pelletier committed
158 159
    def _redirectToOriginalForm(self, REQUEST=None, form_id=None, dialog_id=None,
                                query_string=None,
160
                                no_reset=False, no_report_depth=False):
Vincent Pelletier's avatar
Vincent Pelletier committed
161 162
      """Redirect to the original form or dialog, using the information given
         as parameters.
163 164
         (Actually does not redirect  in the HTTP meaning because of URL
         limitation problems.)
Vincent Pelletier's avatar
Vincent Pelletier committed
165 166 167

         DEPRECATED parameters :
         query_string is used to transmit parameters from caller to callee.
168
         If no_reset is True, replace reset parameters with noreset.
Vincent Pelletier's avatar
Vincent Pelletier committed
169 170
         If no_report_depth is True, replace report_depth parameters with
         noreport_depth.
171 172 173 174
      """
      if REQUEST is None:
        return

Kazuhiko Shiozaki's avatar
Kazuhiko Shiozaki committed
175 176 177 178 179 180 181
      form = REQUEST.form
      if no_reset and form.has_key('reset'):
        form['noreset'] = form['reset'] # Kept for compatibility - might no be used anymore
        del form['reset']
      if no_report_depth and form.has_key('report_depth'):
        form['noreport_depth'] = form['report_depth'] # Kept for compatibility - might no be used anymore
        del form['report_depth']
Vincent Pelletier's avatar
Vincent Pelletier committed
182

183
      if query_string is not None:
184 185
        warnings.warn('DEPRECATED: _redirectToOriginalForm got called with a query_string. The variables must be passed in REQUEST.form.',
                      DeprecationWarning)
186
      context = REQUEST['PARENTS'][0]
Vincent Pelletier's avatar
Vincent Pelletier committed
187
      form_id = dialog_id or REQUEST.get('dialog_id', None) or form_id or REQUEST.get('form_id', 'view')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
188
      return getattr(context, form_id)()
189

190 191
    security.declareProtected(ERP5Permissions.View, 'getSelectionNameList')
    def getSelectionNameList(self, context=None, REQUEST=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
192 193 194
      """
        Returns the selection names of the current user.
      """
195 196
      if self.isMemcachedUsed():
        return []
197
      return sorted(self._getSelectionNameListFromContainer())
198

199 200 201 202 203 204 205 206
    # backward compatibility
    security.declareProtected(ERP5Permissions.View, 'getSelectionNames')
    def getSelectionNames(self, context=None, REQUEST=None):
      warnings.warn("getSelectionNames() is deprecated.\n"
                    "Please use getSelectionNameList() instead.",
                    DeprecationWarning)
      return self.getSelectionNameList(context, REQUEST)

207 208 209 210 211 212 213 214
    security.declareProtected(ERP5Permissions.View, 'callSelectionFor')
    def callSelectionFor(self, selection_name, context=None, REQUEST=None):
      if context is None: context = self
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is None:
        return None
      return selection(context=context)

Jean-Paul Smets's avatar
Jean-Paul Smets committed
215 216 217 218 219
    security.declareProtected(ERP5Permissions.View, 'getSelectionFor')
    def getSelectionFor(self, selection_name, REQUEST=None):
      """
        Returns the selection instance for a given selection_name
      """
220 221 222 223 224
      if isinstance(selection_name, (tuple, list)):
        selection_name = selection_name[0]
      selection = self._getSelectionFromContainer(selection_name)
      if selection is not None:
        return selection.__of__(self)
225

226 227 228
    def __getitem__(self, key):
        return self.getSelectionParamsFor(key)

Jean-Paul Smets's avatar
Jean-Paul Smets committed
229 230 231 232 233
    security.declareProtected(ERP5Permissions.View, 'setSelectionFor')
    def setSelectionFor(self, selection_name, selection_object, REQUEST=None):
      """
        Sets the selection instance for a given selection_name
      """
234 235
      if selection_object != None:
        # Set the name so that this selection itself can get its own name.
236
        selection_object.edit(name=selection_name)
237

238 239
      if self.getSelectionFor(selection_name) != selection_object:
        self._setSelectionToContainer(selection_name, selection_object)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
240

241 242
    security.declareProtected(ERP5Permissions.View, 'getSelectionParamsFor')
    def getSelectionParamsFor(self, selection_name, params=None, REQUEST=None):
243 244 245
      """
        Returns the params in the selection
      """
246
      if params is None: params = {}
247 248
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
249 250
        if len(selection.params) > 0:
          return selection.getParams()
251 252 253 254
        else:
          return params
      else:
        return params
255 256

    # backward compatibility
257 258
    security.declareProtected(ERP5Permissions.View, 'getSelectionParams')
    getSelectionParams = getSelectionParamsFor
259

Jean-Paul Smets's avatar
Jean-Paul Smets committed
260 261 262 263 264 265
    security.declareProtected(ERP5Permissions.View, 'setSelectionParamsFor')
    def setSelectionParamsFor(self, selection_name, params, REQUEST=None):
      """
        Sets the selection params for a given selection_name
      """
      selection_object = self.getSelectionFor(selection_name, REQUEST)
266
      if selection_object is not None:
Jean-Paul Smets's avatar
Jean-Paul Smets committed
267 268 269 270 271
        selection_object.edit(params=params)
      else:
        selection_object = Selection(params=params)
      self.setSelectionFor(selection_name, selection_object, REQUEST)

272
    security.declareProtected(ERP5Permissions.View, 'getSelectionDomainDictFor')
273 274 275 276 277 278 279 280 281 282 283
    def getSelectionDomainDictFor(self, selection_name, REQUEST=None):
      """
        Returns the Domain dict for a given selection_name
      """
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
        try:
          return selection.getDomain().asDomainDict
        except AttributeError:
          return {}

284
    security.declareProtected(ERP5Permissions.View, 'getSelectionReportDictFor')
285 286 287 288 289 290 291 292 293 294 295
    def getSelectionReportDictFor(self, selection_name, REQUEST=None):
      """
        Returns the Report dict for a given selection_name
      """
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
        try:
          return selection.getReport().asDomainDict
        except AttributeError:
          return {}

Jean-Paul Smets's avatar
Jean-Paul Smets committed
296 297 298
    security.declareProtected(ERP5Permissions.View, 'setSelectionCheckedUidsFor')
    def setSelectionCheckedUidsFor(self, selection_name, checked_uids, REQUEST=None):
      """
299
        Sets the checked uids for a given selection_name
Jean-Paul Smets's avatar
Jean-Paul Smets committed
300 301 302 303 304 305 306 307
      """
      selection_object = self.getSelectionFor(selection_name, REQUEST)
      if selection_object:
        selection_object.edit(checked_uids=checked_uids)
      else:
        selection_object = Selection(checked_uids=checked_uids)
      self.setSelectionFor(selection_name, selection_object, REQUEST)

308
    security.declareProtected(ERP5Permissions.View, 'updateSelectionCheckedUidList')
309 310
    def updateSelectionCheckedUidList(self, selection_name, listbox_uid, uids, REQUEST=None):
      """
311 312
        Updates the unchecked uids(listbox_uids) and checked uids (uids)
        for a given selection_name
313 314 315 316 317 318 319 320
      """
      if listbox_uid is None:
        listbox_uid = []
      if uids is None:
        uids = []
      self.uncheckAll(selection_name,listbox_uid,REQUEST=REQUEST)
      self.checkAll(selection_name,uids,REQUEST=REQUEST)

321 322 323
    security.declareProtected(ERP5Permissions.View, 'getSelectionCheckedUidsFor')
    def getSelectionCheckedUidsFor(self, selection_name, REQUEST=None):
      """
324
        Returns the checked uids for a given selection_name
325 326 327
      """
      selection_object = self.getSelectionFor(selection_name, REQUEST)
      if selection_object:
328
        return selection_object.getCheckedUids()
329 330 331
      return []

    security.declareProtected(ERP5Permissions.View, 'checkAll')
332
    def checkAll(self, list_selection_name, listbox_uid=[], REQUEST=None,
333
                 query_string=None, form_id=None):
334
      """
335
        Check uids in a given listbox_uid list for a given list_selection_name
336
      """
337
      selection_object = self.getSelectionFor(list_selection_name, REQUEST)
338 339
      if selection_object:
        selection_uid_dict = {}
340
        for uid in selection_object.checked_uids:
341 342
          selection_uid_dict[uid] = 1
        for uid in listbox_uid:
343 344
          try:
            selection_uid_dict[int(uid)] = 1
345
          except ValueError:
346
            selection_uid_dict[uid] = 1
347
        self.setSelectionCheckedUidsFor(list_selection_name, selection_uid_dict.keys(), REQUEST=REQUEST)
348 349 350
      if REQUEST is not None:
        return self._redirectToOriginalForm(REQUEST=REQUEST, form_id=form_id,
                                            query_string=query_string, no_reset=True)
351 352

    security.declareProtected(ERP5Permissions.View, 'uncheckAll')
353
    def uncheckAll(self, list_selection_name, listbox_uid=[], REQUEST=None,
354
                   query_string=None, form_id=None):
355
      """
356
        Uncheck uids in a given listbox_uid list for a given list_selection_name
357
      """
358
      selection_object = self.getSelectionFor(list_selection_name, REQUEST)
359 360
      if selection_object:
        selection_uid_dict = {}
361
        for uid in selection_object.checked_uids:
362 363
          selection_uid_dict[uid] = 1
        for uid in listbox_uid:
364 365 366
          try:
            if selection_uid_dict.has_key(int(uid)): del selection_uid_dict[int(uid)]
          except ValueError:
367
            if selection_uid_dict.has_key(uid): del selection_uid_dict[uid]
368
        self.setSelectionCheckedUidsFor(list_selection_name, selection_uid_dict.keys(), REQUEST=REQUEST)
369 370 371
      if REQUEST is not None:
        return self._redirectToOriginalForm(REQUEST=REQUEST, form_id=form_id,
                                            query_string=query_string, no_reset=True)
372

Jean-Paul Smets's avatar
Jean-Paul Smets committed
373 374 375 376 377 378 379
    security.declareProtected(ERP5Permissions.View, 'getSelectionListUrlFor')
    def getSelectionListUrlFor(self, selection_name, REQUEST=None):
      """
        Returns the URL of the list mode of selection instance
      """
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection:
380
        return selection.getListUrl()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
381 382
      else:
        return None
383

384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
    security.declareProtected(ERP5Permissions.View, 'getSelectionInvertModeFor')
    def getSelectionInvertModeFor(self, selection_name, REQUEST=None):
      """Get the 'invert_mode' parameter of a selection.
      """
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
        return selection.isInvertMode()
      return 0

    security.declareProtected(ERP5Permissions.View, 'setSelectionInvertModeFor')
    def setSelectionInvertModeFor(self, selection_name,
                                  invert_mode, REQUEST=None):
      """Change the 'invert_mode' parameter of a selection.
      """
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
        selection.edit(invert_mode=invert_mode)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
401

402
    security.declareProtected(ERP5Permissions.View, 'getSelectionInvertModeUidListFor')
403 404 405 406 407 408 409 410
    def getSelectionInvertModeUidListFor(self, selection_name, REQUEST=None):
      """Get the 'invert_mode' parameter of a selection.
      """
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
        return selection.getInvertModeUidList()
      return 0

411 412 413 414 415 416 417 418 419
    security.declareProtected(ERP5Permissions.View, 'getSelectionIndexFor')
    def getSelectionIndexFor(self, selection_name, REQUEST=None):
      """Get the 'index' parameter of a selection.
      """
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
        return selection.getIndex()
      return None

Jean-Paul Smets's avatar
Jean-Paul Smets committed
420 421 422 423 424 425 426
    security.declareProtected(ERP5Permissions.View, 'setSelectionToIds')
    def setSelectionToIds(self, selection_name, selection_uids, REQUEST=None):
      """
        Sets the selection to a small list of uids of documents
      """
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
427
        selection.edit(invert_mode=1, uids=selection_uids, checked_uids=selection_uids)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
428 429

    security.declareProtected(ERP5Permissions.View, 'setSelectionToAll')
430 431
    def setSelectionToAll(self, selection_name, REQUEST=None,
                          reset_domain_tree=False, reset_report_tree=False):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
432 433 434 435 436
      """
        Resets the selection
      """
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
437
        selection.edit(invert_mode=0, params={}, checked_uids=[], report_opened=1)
438
        if reset_domain_tree:
439
          selection.edit(domain=None, domain_path=None, domain_list=None)
440
        if reset_report_tree:
441
          selection.edit(report=None, report_path=None, report_list=None)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
442 443 444 445 446 447 448 449 450 451 452

    security.declareProtected(ERP5Permissions.View, 'setSelectionSortOrder')
    def setSelectionSortOrder(self, selection_name, sort_on, REQUEST=None):
      """
        Defines the sort order of the selection
      """
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
        selection.edit(sort_on=sort_on)

    security.declareProtected(ERP5Permissions.View, 'setSelectionQuickSortOrder')
453
    def setSelectionQuickSortOrder(self, selection_name=None, sort_on=None, REQUEST=None,
454
                                   query_string=None, form_id=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
455 456 457 458
      """
        Defines the sort order of the selection directly from the listbox
        In this method, sort_on is just a string that comes from url
      """
459 460
      # selection_name, sort_on and form_id params are kept only for bacward compatibilty
      # as some test call setSelectionQuickSortOrder in url with these params
Aurel's avatar
Aurel committed
461
      listbox_id = None
462 463
      if REQUEST is not None:
        form = REQUEST.form
464
      if sort_on is None:
Kazuhiko Shiozaki's avatar
Kazuhiko Shiozaki committed
465
        listbox_id, sort_on = form["setSelectionQuickSortOrder"].split(".", 1)
466

467 468 469 470 471 472
      if REQUEST is not None:
        if listbox_id is not None:
            selection_name_key = "%s_list_selection_name" %listbox_id
            selection_name = form[selection_name_key]
        elif selection_name is None:
            selection_name = form['selection_name']
Aurel's avatar
Aurel committed
473
          
Jean-Paul Smets's avatar
Jean-Paul Smets committed
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
        current_sort_on = self.getSelectionSortOrder(selection_name)
        # We must first switch from asc to desc and vice-versa if sort_order exists
        # in selection
        n = 0
        for current in current_sort_on:
          if current[0] == sort_on:
            n = 1
            if current[1] == 'ascending':
              new_sort_on = [(sort_on, 'descending')]
              break
            else:
              new_sort_on = [(sort_on,'ascending')]
              break
        # And if no one exists, we just set ascending sort
        if n == 0:
          new_sort_on = [(sort_on,'ascending')]
        selection.edit(sort_on=new_sort_on)

494
      if REQUEST is not None:
Kazuhiko Shiozaki's avatar
Kazuhiko Shiozaki committed
495 496
        if form.has_key('listbox_uid') and \
            form.has_key('uids'):
497 498 499
          self.uncheckAll(selection_name, REQUEST.get('listbox_uid'))
          self.checkAll(selection_name, REQUEST.get('uids'))

500 501
        return self._redirectToOriginalForm(REQUEST=REQUEST, form_id=form_id,
                                            query_string=query_string, no_reset=True)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
502 503 504 505 506 507 508 509

    security.declareProtected(ERP5Permissions.View, 'getSelectionSortOrder')
    def getSelectionSortOrder(self, selection_name, REQUEST=None):
      """
        Returns the sort order of the selection
      """
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is None: return ()
510
      return selection.sort_on
Jean-Paul Smets's avatar
Jean-Paul Smets committed
511 512 513 514 515 516 517 518 519 520

    security.declareProtected(ERP5Permissions.View, 'setSelectionColumns')
    def setSelectionColumns(self, selection_name, columns, REQUEST=None):
      """
        Defines the columns in the selection
      """
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      selection.edit(columns=columns)

    security.declareProtected(ERP5Permissions.View, 'getSelectionColumns')
521
    def getSelectionColumns(self, selection_name, columns=None, REQUEST=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
522
      """
523 524
        Returns the columns in the selection if not empty, otherwise
        returns the value of columns argument
Jean-Paul Smets's avatar
Jean-Paul Smets committed
525
      """
526
      if columns is None: columns = []
Jean-Paul Smets's avatar
Jean-Paul Smets committed
527 528
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
529 530
        if len(selection.columns) > 0:
          return selection.columns
531
      return columns
Jean-Paul Smets's avatar
Jean-Paul Smets committed
532 533 534 535 536 537 538 539 540 541 542


    security.declareProtected(ERP5Permissions.View, 'setSelectionStats')
    def setSelectionStats(self, selection_name, stats, REQUEST=None):
      """
        Defines the stats in the selection
      """
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      selection.edit(stats=stats)

    security.declareProtected(ERP5Permissions.View, 'getSelectionStats')
543
    def getSelectionStats(self, selection_name, stats=_MARKER, REQUEST=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
544 545 546
      """
        Returns the stats in the selection
      """
547 548 549 550
      if stats is not _MARKER:
        default_stats = stats
      else:
        default_stats = [' '] * 6
Jean-Paul Smets's avatar
Jean-Paul Smets committed
551 552
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
553
        return getattr(aq_base(selection), 'stats', default_stats)
554
      return default_stats
Jean-Paul Smets's avatar
Jean-Paul Smets committed
555 556 557 558 559 560 561 562 563 564 565


    security.declareProtected(ERP5Permissions.View, 'viewFirst')
    def viewFirst(self, selection_index='', selection_name='', form_id='view', REQUEST=None):
      """
        Access first item in a selection
      """
      if not REQUEST:
        REQUEST = get_request()
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection:
566
        method = self.unrestrictedTraverse(selection.method_path)
567
        selection_list = selection(method = method, context=self, REQUEST=REQUEST)
568 569 570 571
        if len(selection_list):
          o = selection_list[0]
          url = o.absolute_url()
        else:
572
          url = REQUEST.getURL()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
573
      else:
574
        url = REQUEST.getURL()
575 576
      ignore_layout = int(REQUEST.get('ignore_layout', 0))
      url = '%s/%s?selection_index=%s&selection_name=%s&ignore_layout:int=%s' % (url, form_id, 0, selection_name, ignore_layout)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
577 578 579 580 581
      REQUEST.RESPONSE.redirect(url)

    security.declareProtected(ERP5Permissions.View, 'viewLast')
    def viewLast(self, selection_index='', selection_name='', form_id='view', REQUEST=None):
      """
582
        Access last item in a selection
Jean-Paul Smets's avatar
Jean-Paul Smets committed
583 584 585 586 587
      """
      if not REQUEST:
        REQUEST = get_request()
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection:
588
        method = self.unrestrictedTraverse(selection.method_path)
589
        selection_list = selection(method = method, context=self, REQUEST=REQUEST)
590 591 592 593 594
        if len(selection_list):
          o = selection_list[-1]
          url = o.absolute_url()
        else:
          url = REQUEST.getURL()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
595
      else:
596
        url = REQUEST.getURL()
597 598
      ignore_layout = int(REQUEST.get('ignore_layout', 0))
      url = '%s/%s?selection_index=%s&selection_name=%s&ignore_layout:int=%s' % (url, form_id, -1, selection_name, ignore_layout)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
599 600 601 602 603 604 605 606 607 608 609
      REQUEST.RESPONSE.redirect(url)

    security.declareProtected(ERP5Permissions.View, 'viewNext')
    def viewNext(self, selection_index='', selection_name='', form_id='view', REQUEST=None):
      """
        Access next item in a selection
      """
      if not REQUEST:
        REQUEST = get_request()
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection:
610
        method = self.unrestrictedTraverse(selection.method_path)
611
        selection_list = selection(method = method, context=self, REQUEST=REQUEST)
Aurel's avatar
Aurel committed
612
        if len(selection_list):
613 614 615 616
          o = selection_list[(int(selection_index) + 1) % len(selection_list)]
          url = o.absolute_url()
        else:
          url = REQUEST.getURL()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
617
      else:
618
        url = REQUEST.getURL()
619 620
      ignore_layout = int(REQUEST.get('ignore_layout', 0))
      url = '%s/%s?selection_index=%s&selection_name=%s&ignore_layout:int=%s' % (url, form_id, int(selection_index) + 1, selection_name, ignore_layout)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
621 622 623 624 625 626 627 628 629 630 631
      REQUEST.RESPONSE.redirect(url)

    security.declareProtected(ERP5Permissions.View, 'viewPrevious')
    def viewPrevious(self, selection_index='', selection_name='', form_id='view', REQUEST=None):
      """
        Access previous item in a selection
      """
      if not REQUEST:
        REQUEST = get_request()
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection:
632
        method = self.unrestrictedTraverse(selection.method_path)
633
        selection_list = selection(method = method, context=self, REQUEST=REQUEST)
634 635 636 637 638
        if len(selection_list):
          o = selection_list[(int(selection_index) - 1) % len(selection_list)]
          url = o.absolute_url()
        else:
          url = REQUEST.getURL()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
639
      else:
640
        url = REQUEST.getURL()
641 642
      ignore_layout = int(REQUEST.get('ignore_layout', 0))
      url = '%s/%s?selection_index=%s&selection_name=%s&ignore_layout:int=%s' % (url, form_id, int(selection_index) - 1, selection_name, ignore_layout)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
643 644 645 646
      REQUEST.RESPONSE.redirect(url)


    # ListBox related methods
647 648

    security.declareProtected(ERP5Permissions.View, 'firstPage')
Vincent Pelletier's avatar
Vincent Pelletier committed
649
    def firstPage(self, list_selection_name, listbox_uid, uids=None, REQUEST=None):
650 651
      """
        Access the first page of a list
652 653 654
        XXX: As its complementary (lastPage) is broken, this method is
        probably not used either. If so, it should be removed along with
        lastPage.
655 656
      """
      if uids is None: uids = []
Vincent Pelletier's avatar
Vincent Pelletier committed
657 658 659
      REQUEST.form['list_start'] = 0
      self.uncheckAll(list_selection_name, listbox_uid)
      return self.checkAll(list_selection_name, uids, REQUEST=REQUEST)
660 661

    security.declareProtected(ERP5Permissions.View, 'lastPage')
Vincent Pelletier's avatar
Vincent Pelletier committed
662
    def lastPage(self, list_selection_name, listbox_uid, uids=None, REQUEST=None):
663 664
      """
        Access the last page of a list
665 666 667
        XXX: This method is broken, since "total_size" field is not
        present in the listbox rendering any longer. It should be
        removed.
668 669
      """
      if uids is None: uids = []
Vincent Pelletier's avatar
Vincent Pelletier committed
670
      selection = self.getSelectionFor(list_selection_name, REQUEST)
671 672 673 674 675 676 677 678 679 680 681 682
      if selection is not None:
        params = selection.getParams()
        # XXX This will not work if the number of lines shown in the listbox is greater
        #       than the BIG_INT constan. Such a case has low probability but is not
        #       impossible. If you are in this case, send me a mail ! -- Kev
        BIG_INT = 10000000
        last_page_start = BIG_INT
        total_lines = REQUEST.form.get('total_size', BIG_INT)
        if total_lines != BIG_INT:
          lines_per_page  = params.get('list_lines', 1)
          last_page_start = int(total_lines) - (int(total_lines) % int(lines_per_page))
        REQUEST.form['list_start'] = last_page_start
Vincent Pelletier's avatar
Vincent Pelletier committed
683 684
      self.uncheckAll(list_selection_name, listbox_uid)
      return self.checkAll(list_selection_name, uids, REQUEST=REQUEST)
685

Jean-Paul Smets's avatar
Jean-Paul Smets committed
686
    security.declareProtected(ERP5Permissions.View, 'nextPage')
Vincent Pelletier's avatar
Vincent Pelletier committed
687
    def nextPage(self, list_selection_name, listbox_uid, uids=None, REQUEST=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
688 689 690
      """
        Access the next page of a list
      """
691
      if uids is None: uids = []
Vincent Pelletier's avatar
Vincent Pelletier committed
692
      selection = self.getSelectionFor(list_selection_name, REQUEST)
693 694
      if selection is not None:
        params = selection.getParams()
695 696 697
        lines = int(params.get('list_lines', 0))
        form = REQUEST.form
        if form.has_key('page_start'):
698 699 700 701
          try:
            list_start = (int(form.pop('page_start', 0)) - 1) * lines
          except ValueError:
            list_start = 0
702 703
        else:
          list_start = int(form.pop('list_start', 0))
704
        params['list_start'] = max(list_start + lines, 0)
705
        selection.edit(params=params)
Vincent Pelletier's avatar
Vincent Pelletier committed
706 707
      self.uncheckAll(list_selection_name, listbox_uid)
      return self.checkAll(list_selection_name, uids, REQUEST=REQUEST)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
708 709

    security.declareProtected(ERP5Permissions.View, 'previousPage')
Vincent Pelletier's avatar
Vincent Pelletier committed
710
    def previousPage(self, list_selection_name, listbox_uid, uids=None, REQUEST=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
711 712 713
      """
        Access the previous page of a list
      """
714
      if uids is None: uids = []
Vincent Pelletier's avatar
Vincent Pelletier committed
715
      selection = self.getSelectionFor(list_selection_name, REQUEST)
716 717
      if selection is not None:
        params = selection.getParams()
718 719 720
        lines = int(params.get('list_lines', 0))
        form = REQUEST.form
        if form.has_key('page_start'):
721 722 723 724
          try:
            list_start = (int(form.pop('page_start', 0)) - 1) * lines
          except ValueError:
            list_start = 0
725 726 727
        else:
          list_start = int(form.pop('list_start', 0))
        params['list_start'] = max(list_start - lines, 0)
728
        selection.edit(params=params)
Vincent Pelletier's avatar
Vincent Pelletier committed
729 730
      self.uncheckAll(list_selection_name, listbox_uid)
      return self.checkAll(list_selection_name, uids, REQUEST=REQUEST)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
731 732

    security.declareProtected(ERP5Permissions.View, 'setPage')
Vincent Pelletier's avatar
Vincent Pelletier committed
733
    def setPage(self, list_selection_name, listbox_uid, query_string=None, uids=None, REQUEST=None):
734
      """
Jean-Paul Smets's avatar
Jean-Paul Smets committed
735
         Sets the current displayed page in a selection
736
      """
737
      if uids is None: uids = []
Vincent Pelletier's avatar
Vincent Pelletier committed
738
      selection = self.getSelectionFor(list_selection_name, REQUEST)
739 740
      if selection is not None:
        params = selection.getParams()
741 742 743
        lines = int(params.get('list_lines', 0))
        form = REQUEST.form
        if form.has_key('page_start'):
744 745 746 747
          try:
            list_start = (int(form.pop('page_start', 0)) - 1) * lines
          except ValueError:
            list_start = 0
748 749
        else:
          list_start = int(form.pop('list_start', 0))
750
        params['list_start'] = max(list_start, 0)
751 752
        selection.edit(params=params)
        self.uncheckAll(list_selection_name, listbox_uid)
Vincent Pelletier's avatar
Vincent Pelletier committed
753
      return self.checkAll(list_selection_name, uids, REQUEST=REQUEST, query_string=query_string)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
754

755
    # PlanningBox related methods
756 757
    security.declareProtected(ERP5Permissions.View, 'setLanePath')
    def setLanePath(self, uids=None, REQUEST=None, form_id=None,
758
                     query_string=None):
759 760 761
      """
      Set graphic zoom level in PlanningBox
      """
762 763
      if uids is None:
        uids = []
764 765 766 767 768
      request = REQUEST
      selection_name=request.list_selection_name
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
        params = selection.getParams()
769 770 771
        lane_path = request.form.get('lane_path', None)
        if lane_path is None:
          # If lane_path is not defined try to 
772
          # use the last one from params
773 774 775 776 777 778
          lane_path = params.get('lane_path',1)
        bound_start = request.form.get('bound_start', None)
        if bound_start is not None:
          params['bound_start'] = bound_start
        params['lane_path'] = lane_path     
        params['zoom_variation'] = 0
779
        selection.edit(params=params)
780
      if REQUEST is not None:
781 782 783
        return self._redirectToOriginalForm(REQUEST=REQUEST,
                                            form_id=form_id,
                                            query_string=query_string)
784

785 786
    security.declareProtected(ERP5Permissions.View, 'nextLanePage')
    def nextLanePage(self, uids=None, REQUEST=None, form_id=None, query_string=None):
787 788 789
      """
      Set next graphic zoom start in PlanningBox
      """
790 791
      if uids is None:
        uids = []
792 793 794 795 796
      request = REQUEST
      selection_name=request.list_selection_name
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
        params = selection.getParams()
797
        params['bound_variation'] = 1
798
        selection.edit(params=params)
799
      if REQUEST is not None:
800 801 802
        return self._redirectToOriginalForm(REQUEST=REQUEST,
                                            form_id=form_id,
                                             query_string=query_string)
803

804 805
    security.declareProtected(ERP5Permissions.View, 'previousLanePage')
    def previousLanePage(self, uids=None, REQUEST=None, form_id=None, query_string=None):
806 807 808
      """
      Set previous graphic zoom in PlanningBox
      """
809 810
      if uids is None:
        uids = []
811 812 813 814 815
      request = REQUEST
      selection_name=request.list_selection_name
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
        params = selection.getParams()
816
        params['bound_variation'] = -1
817 818 819 820 821
        selection.edit(params=params)
      if REQUEST is not None:
        return self._redirectToOriginalForm(REQUEST=REQUEST,
                                            form_id=form_id,
                                             query_string=query_string)
822

Jean-Paul Smets's avatar
Jean-Paul Smets committed
823
    security.declareProtected(ERP5Permissions.View, 'setDomainRoot')
824
    def setDomainRoot(self, REQUEST, form_id=None, query_string=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
825 826 827
      """
        Sets the root domain for the current selection
      """
828
      selection_name = REQUEST.list_selection_name
Jean-Paul Smets's avatar
Jean-Paul Smets committed
829
      selection = self.getSelectionFor(selection_name, REQUEST)
830
      root_url = REQUEST.form.get('domain_root_url','portal_categories')
831
      selection.edit(domain_path=root_url, domain_list=())
Jean-Paul Smets's avatar
Jean-Paul Smets committed
832

833 834 835
      if REQUEST is not None:
        return self._redirectToOriginalForm(REQUEST=REQUEST, form_id=form_id,
                                            query_string=query_string)
836

837 838 839 840 841 842
    security.declareProtected(ERP5Permissions.View, 'setDomainRootFromParam')
    def setDomainRootFromParam(self, REQUEST, selection_name, domain_root):
      if REQUEST is None:
        return
      selection = self.getSelectionFor(selection_name, REQUEST)
      selection.edit(domain_path=domain_root, domain_list=())
Jean-Paul Smets's avatar
Jean-Paul Smets committed
843

844
    security.declareProtected(ERP5Permissions.View, 'unfoldDomain')
845
    def unfoldDomain(self, REQUEST, form_id=None, query_string=None):
846 847 848
      """
        Unfold domain for the current selection
      """
849
      selection_name = REQUEST.list_selection_name
Jean-Paul Smets's avatar
Jean-Paul Smets committed
850
      selection = self.getSelectionFor(selection_name, REQUEST)
851 852
      domain_url = REQUEST.form.get('domain_url',None)
      domain_depth = REQUEST.form.get('domain_depth',0)
853 854
      domain_list = list(selection.getDomainList())
      domain_list = domain_list[0:min(domain_depth, len(domain_list))]
855
      if isinstance(domain_url, str):
856
        selection.edit(domain_list = domain_list + [domain_url])
Jean-Paul Smets's avatar
Jean-Paul Smets committed
857

858 859 860
      if REQUEST is not None:
        return self._redirectToOriginalForm(REQUEST=REQUEST, form_id=form_id,
                                            query_string=query_string)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
861

862
    security.declareProtected(ERP5Permissions.View, 'foldDomain')
863
    def foldDomain(self, REQUEST, form_id=None, query_string=None):
864 865 866
      """
        Fold domain for the current selection
      """
867
      selection_name = REQUEST.list_selection_name
868
      selection = self.getSelectionFor(selection_name, REQUEST)
869 870
      domain_url = REQUEST.form.get('domain_url',None)
      domain_depth = REQUEST.form.get('domain_depth',0)
871 872
      domain_list = list(selection.getDomainList())
      domain_list = domain_list[0:min(domain_depth, len(domain_list))]
873
      selection.edit(domain_list=[x for x in domain_list if x != domain_url])
874

875 876 877
      if REQUEST is not None:
        return self._redirectToOriginalForm(REQUEST=REQUEST, form_id=form_id,
                                            query_string=query_string)
878

879

Jean-Paul Smets's avatar
Jean-Paul Smets committed
880
    security.declareProtected(ERP5Permissions.View, 'setReportRoot')
881
    def setReportRoot(self, REQUEST, form_id=None, query_string=None):
882 883 884
      """
        Sets the root report for the current selection
      """
885
      selection_name = REQUEST.list_selection_name
Jean-Paul Smets's avatar
Jean-Paul Smets committed
886
      selection = self.getSelectionFor(selection_name, REQUEST)
887
      root_url = REQUEST.form.get('report_root_url','portal_categories')
888
      selection.edit(report_path=root_url, report_list=())
Jean-Paul Smets's avatar
Jean-Paul Smets committed
889

890 891 892
      if REQUEST is not None:
        return self._redirectToOriginalForm(REQUEST=REQUEST, form_id=form_id,
                                            query_string=query_string)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
893 894

    security.declareProtected(ERP5Permissions.View, 'unfoldReport')
895
    def unfoldReport(self, REQUEST, form_id=None, query_string=None):
896 897 898
      """
        Unfold report for the current selection
      """
899
      selection_name = REQUEST.list_selection_name
Jean-Paul Smets's avatar
Jean-Paul Smets committed
900
      selection = self.getSelectionFor(selection_name, REQUEST)
901
      report_url = REQUEST.form.get('report_url',None)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
902
      if type(report_url) == type('a'):
903
        selection.edit(report_list=list(selection.getReportList()) + [report_url])
Jean-Paul Smets's avatar
Jean-Paul Smets committed
904

905 906 907
      return self._redirectToOriginalForm(REQUEST=REQUEST, form_id=form_id,
                                          query_string=query_string,
                                          no_report_depth=True)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
908 909

    security.declareProtected(ERP5Permissions.View, 'foldReport')
910
    def foldReport(self, REQUEST, form_id=None, query_string=None):
911 912 913
      """
        Fold domain for the current selection
      """
914
      selection_name = REQUEST.list_selection_name
Jean-Paul Smets's avatar
Jean-Paul Smets committed
915
      selection = self.getSelectionFor(selection_name, REQUEST)
916
      report_url = REQUEST.form.get('report_url',None)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
917
      if type(report_url) == type('a'):
918
        report_list = selection.getReportList()
919
        selection.edit(report_list=[x for x in report_list if x != report_url])
Jean-Paul Smets's avatar
Jean-Paul Smets committed
920

921 922 923
      return self._redirectToOriginalForm(REQUEST=REQUEST, form_id=form_id,
                                          query_string=query_string,
                                          no_report_depth=True)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
924

925 926 927 928
    security.declareProtected(ERP5Permissions.View, 'getListboxDisplayMode')
    def getListboxDisplayMode(self, selection_name, REQUEST=None):
      if REQUEST is None:
        REQUEST = get_request()
929
      selection = self.getSelectionFor(selection_name, REQUEST)
930

931 932 933 934 935
      if getattr(selection, 'report_tree_mode', 0):
        return 'ReportTreeMode'
      elif getattr(selection, 'domain_tree_mode', 0):
        return 'DomainTreeMode'
      return 'FlatListMode'
936

Jean-Paul Smets's avatar
Jean-Paul Smets committed
937
    security.declareProtected(ERP5Permissions.View, 'setListboxDisplayMode')
938
    def setListboxDisplayMode(self, REQUEST, listbox_display_mode,
939 940
                              selection_name=None, redirect=0,
                              form_id=None, query_string=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
941
      """
942
        Toggle display of the listbox
Jean-Paul Smets's avatar
Jean-Paul Smets committed
943 944
      """
      request = REQUEST
945 946 947 948 949 950 951
      # XXX FIXME
      # Dirty fix: we must be able to change the display mode of a listbox
      # in form_view
      # But, form can have multiple listbox...
      # This need to be cleaned
      # Beware, this fix may break the report system...
      # and we don't have test for this
952 953
      # Possible fix: currently, display mode icon are implemented as
      # method. It could be easier to generate them as link (where we
954 955 956 957 958 959 960 961 962
      # can define explicitely parameters through the url).
      try:
        list_selection_name = request.list_selection_name
      except AttributeError:
        pass
      else:
        if list_selection_name is not None:
          selection_name = request.list_selection_name
      # Get the selection
Jean-Paul Smets's avatar
Jean-Paul Smets committed
963
      selection = self.getSelectionFor(selection_name, REQUEST)
964 965
      if selection is None:
        selection = Selection()
966
        self.setSelectionFor(selection_name, selection, REQUEST=REQUEST)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
967 968 969 970 971 972 973 974 975 976 977 978 979

      if listbox_display_mode == 'FlatListMode':
        flat_list_mode = 1
        domain_tree_mode = 0
        report_tree_mode = 0
      elif listbox_display_mode == 'DomainTreeMode':
        flat_list_mode = 0
        domain_tree_mode = 1
        report_tree_mode = 0
      elif listbox_display_mode == 'ReportTreeMode':
        flat_list_mode = 0
        domain_tree_mode = 0
        report_tree_mode = 1
980 981 982
      else:
        flat_list_mode = 0
        domain_tree_mode = 0
983
        report_tree_mode = 0
984

985 986 987
      selection.edit(flat_list_mode=flat_list_mode,
                     domain_tree_mode=domain_tree_mode,
                     report_tree_mode=report_tree_mode)
988
      # It is better to reset the query when changing the display mode.
989 990
      params = selection.getParams()
      if 'where_expression' in params: del params['where_expression']
991
      selection.edit(params=params)
992

993
      if redirect:
994 995 996
        return self._redirectToOriginalForm(REQUEST=request, form_id=form_id,
                                            query_string=query_string,
                                            no_reset=True)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
997 998

    security.declareProtected(ERP5Permissions.View, 'setFlatListMode')
999
    def setFlatListMode(self, REQUEST, selection_name=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1000 1001 1002
      """
        Set display of the listbox to FlatList mode
      """
1003
      return self.setListboxDisplayMode(
1004
                       REQUEST=REQUEST, listbox_display_mode='FlatListMode',
1005
                       selection_name=selection_name, redirect=1)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1006 1007

    security.declareProtected(ERP5Permissions.View, 'setDomainTreeMode')
1008
    def setDomainTreeMode(self, REQUEST, selection_name=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1009 1010 1011
      """
         Set display of the listbox to DomainTree mode
      """
1012
      return self.setListboxDisplayMode(
1013
                       REQUEST=REQUEST, listbox_display_mode='DomainTreeMode',
1014
                       selection_name=selection_name, redirect=1)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1015 1016

    security.declareProtected(ERP5Permissions.View, 'setReportTreeMode')
1017
    def setReportTreeMode(self, REQUEST, selection_name=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1018 1019 1020
      """
        Set display of the listbox to ReportTree mode
      """
1021 1022 1023
      return self.setListboxDisplayMode(
                       REQUEST=REQUEST, listbox_display_mode='ReportTreeMode',
                       selection_name=selection_name, redirect=1)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1024

1025 1026 1027 1028 1029 1030
    security.declareProtected(ERP5Permissions.View, 'getSelectionSelectedValueList')
    def getSelectionSelectedValueList(self, selection_name, REQUEST=None, selection_method=None, context=None):
      """
        Get the list of values selected for 'selection_name'
      """
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
1031 1032
      if selection is None:
        return []
1033
      return selection(method=selection_method, context=context, REQUEST=REQUEST)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1034

1035 1036 1037 1038 1039 1040
    security.declareProtected(ERP5Permissions.View, 'getSelectionCheckedValueList')
    def getSelectionCheckedValueList(self, selection_name, REQUEST=None):
      """
        Get the list of values checked for 'selection_name'
      """
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
1041 1042
      if selection is None:
        return []
1043
      uid_list = selection.getCheckedUids()
1044 1045 1046 1047 1048 1049 1050 1051 1052 1053
      value_list = self.portal_catalog.getObjectList(uid_list)
      return value_list

    security.declareProtected(ERP5Permissions.View, 'getSelectionValueList')
    def getSelectionValueList(self, selection_name, REQUEST=None, selection_method=None, context=None):
      """
        Get the list of values checked or selected for 'selection_name'
      """
      value_list = self.getSelectionCheckedValueList(selection_name, REQUEST=REQUEST)
      if len(value_list) == 0:
1054 1055
        value_list = self.getSelectionSelectedValueList(
                                            selection_name,
1056 1057
                                            REQUEST=REQUEST,
                                            selection_method=selection_method,
1058
                                            context=context)
1059
      return value_list
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1060

Jean-Paul Smets's avatar
Jean-Paul Smets committed
1061 1062 1063
    security.declareProtected(ERP5Permissions.View, 'getSelectionUidList')
    def getSelectionUidList(self, selection_name, REQUEST=None, selection_method=None, context=None):
      """
1064
        Get the list of uids checked or selected for 'selection_name'
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1065
      """
1066
      return [x.getObject().getUid() for x in self.getSelectionValueList(selection_name, REQUEST=REQUEST, selection_method=selection_method, context=context)]
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1067

Sebastien Robin's avatar
Sebastien Robin committed
1068 1069 1070 1071 1072
    security.declareProtected(ERP5Permissions.View, 'selectionHasChanged')
    def selectionHasChanged(self, md5_string, object_uid_list):
      """
        We want to be sure that the selection did not change
      """
1073
      # XXX To avoid the difference of the string representations of int and long,
1074
      # convert each element to a string.
1075 1076 1077 1078
      object_uid_list = [str(x) for x in object_uid_list]
      object_uid_list.sort()
      new_md5_string = md5.new(str(object_uid_list)).hexdigest()
      return md5_string != new_md5_string
Sebastien Robin's avatar
Sebastien Robin committed
1079

Sebastien Robin's avatar
Sebastien Robin committed
1080

1081
    # Related document searching
1082
    def viewSearchRelatedDocumentDialog(self, index, form_id,
1083
                                        REQUEST=None, sub_index=None, **kw):
1084
      """
1085 1086
      Returns a search related document dialog
      A set of forwarders us defined to circumvent limitations of HTML
1087
      """
Romain Courteaud's avatar
Romain Courteaud committed
1088 1089
      if sub_index != None:
        REQUEST.form['sub_index'] = sub_index
1090
      object_path = REQUEST.form['object_path']
1091
      # Find the object which needs to be updated
1092
      o = self.restrictedTraverse(object_path)
1093
      # Find the field which was clicked on
1094
      # Important to get from the object instead of self
1095
      form = getattr(o, form_id)
1096
      field = None
1097 1098
      # Search the correct field
      relation_field_found = 0
1099
      relation_index = 0
1100
      # XXX may be should support another parameter,
1101
      for field in form.get_fields(include_disabled=0):
1102 1103
        if field.get_value('editable', REQUEST=REQUEST):
          try:
1104 1105
           field.get_value('is_relation_field')
          except KeyError:
1106
            pass
1107
          else:
1108 1109 1110 1111 1112
            if index == relation_index:
              relation_field_found = 1
              break
            else:
              relation_index += 1
1113
      if not relation_field_found:
1114
        # We didn't find the field...
1115
        raise SelectionError, "SelectionTool: can not find the relation" \
1116
                              " field %s" % index
1117 1118 1119 1120
      else:
        # Field found
        field_key = field.generate_field_key()
        field_value = REQUEST.form[field_key]
1121 1122
        dialog_id = field.get_value('relation_form_id') or \
                                                   'Base_viewRelatedObjectList'
1123
        redirect_form = getattr(o, dialog_id)
1124 1125 1126
        # XXX Hardcoded listbox field
        selection_name = redirect_form.listbox.get_value('selection_name')
        # Reset current selection
1127
        self.setSelectionFor(selection_name, None)
1128 1129 1130 1131


        if (field.get_value('is_multi_relation_field')) and \
           (sub_index is None):
1132 1133 1134 1135
          # user click on the wheel, not on the validation button
          # we need to facilitate user search

          # first: store current field value in the selection
1136
          base_category = field.get_value('base_category')
1137

1138 1139 1140 1141 1142 1143
          property_get_related_uid_method_name = \
            "get%sUidList" % ''.join(['%s%s' % (x[0].upper(), x[1:]) \
                                      for x in base_category.split('_')])
          current_uid_list = getattr(o, property_get_related_uid_method_name)\
                               (portal_type=[x[0] for x in \
                                  field.get_value('portal_type')])
Romain Courteaud's avatar
Romain Courteaud committed
1144 1145 1146
          # Checked current uid
          kw ={}
          kw[field.get_value('catalog_index')] = field_value
1147
          self.setSelectionParamsFor(selection_name,
Vincent Pelletier's avatar
Vincent Pelletier committed
1148 1149 1150
                                     kw.copy())
          self.setSelectionCheckedUidsFor(selection_name,
                                          current_uid_list)
1151
          field_value = str(field_value).splitlines()
1152 1153 1154 1155 1156
          # Prevent displaying useless empty list or list with only one element
          if not field_value:
            field_value = ''
          if len(field_value) == 1:
            field_value = field_value[0]
1157 1158
          if len(field_value) > 1 and isinstance(field_value, type([])):
            field_value = ' OR '.join(field_value)
1159
          REQUEST.form[field_key] = field_value
1160
          portal_status_message = translateString("Please select one (or more) object.")
1161
        else:
1162
          portal_status_message = translateString("Please select one object.")
1163 1164


1165 1166
        # Save the current REQUEST form
        # We can't put FileUpload instances because we can't pickle them
1167 1168 1169 1170
        saved_form_data = {}
        for key, value in REQUEST.form.items():
          if not isinstance(value, FileUpload):
              saved_form_data[key] = value
1171

1172 1173
        base_category = None
        kw = {}
1174
        kw['dialog_id'] = dialog_id
1175 1176 1177 1178 1179 1180 1181 1182 1183
        kw['selection_name'] = selection_name
        kw['selection_index'] = 0 # We start on the first page
        kw['field_id'] = field.id
        parameter_list = field.get_value('parameter_list')
        if len(parameter_list) > 0:
          for k,v in parameter_list:
            kw[k] = v
        kw['reset'] = 0
        kw['base_category'] = field.get_value( 'base_category')
1184
        kw['form_id'] = form_id
1185 1186
        kw[field.get_value('catalog_index')] = field_value
        kw['portal_status_message'] = portal_status_message
1187
        kw['saved_form_data'] = saved_form_data
1188
        kw['ignore_layout'] = int(REQUEST.get('ignore_layout', 0))
1189 1190 1191
        # remove ignore_layout parameter from cancel_url otherwise we
        # will have two ignore_layout parameters after clicking cancel
        # button.
1192 1193 1194
        split_referer = list(urlsplit(REQUEST.get('HTTP_REFERER')))
        split_referer[3] = '&'.join([x for x in \
                                     split_referer[3].split('&') \
1195
                                     if not re.match('^ignore_layout[:=]', x)])
1196
        kw['cancel_url'] = urlunsplit(split_referer)
1197

1198 1199 1200 1201 1202 1203 1204 1205 1206
        proxy_listbox_ids = field.get_value('proxy_listbox_ids')
        REQUEST.set('proxy_listbox_ids', proxy_listbox_ids)
        if len(proxy_listbox_ids) > 0:
          REQUEST.set('proxy_listbox_id', proxy_listbox_ids[0][0])
        else:
          REQUEST.set('proxy_listbox_id', \
                       "Base_viewRelatedObjectListBase/listbox")

        # Empty the selection (uid)
1207 1208
        REQUEST.form = kw # New request form
        # Define new HTTP_REFERER
Romain Courteaud's avatar
Romain Courteaud committed
1209
        REQUEST.HTTP_REFERER = '%s/%s' % (o.absolute_url(),
1210
                                          dialog_id)
1211 1212 1213 1214 1215

        # If we are called from a Web Site, we should return
        # in the context of the Web Section
        if self.getApplicableLayout() is not None:
          return getattr(o.__of__(self.getWebSectionValue()), dialog_id)(REQUEST=REQUEST)
1216
        # Return the search dialog
1217
        return getattr(o, dialog_id)(REQUEST=REQUEST)
1218

1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229
    security.declarePublic('buildSQLJoinExpressionFromDomainSelection')
    def buildSQLJoinExpressionFromDomainSelection(self, selection_domain,
                                                  domain_id=None,
                                                  exclude_domain_id=None,
                                                  category_table_alias='category'):
      if isinstance(selection_domain, DomainSelection):
        warnings.warn("To pass a DomainSelection instance is deprecated.\n"
                      "Please use a domain dict instead.",
                      DeprecationWarning)
      else:
        selection_domain = DomainSelection(selection_domain).__of__(self)
1230 1231
      return selection_domain.asSQLJoinExpression(
          category_table_alias=category_table_alias)
1232 1233 1234

    security.declarePublic('buildSQLExpressionFromDomainSelection')
    def buildSQLExpressionFromDomainSelection(self, selection_domain,
1235
                                              table_map=None, domain_id=None,
1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247
                                              exclude_domain_id=None,
                                              strict_membership=0,
                                              join_table="catalog",
                                              join_column="uid",
                                              base_category=None,
                                              category_table_alias='category'):
      if isinstance(selection_domain, DomainSelection):
        warnings.warn("To pass a DomainSelection instance is deprecated.\n"
                      "Please use a domain dict instead.",
                      DeprecationWarning)
      else:
        selection_domain = DomainSelection(selection_domain).__of__(self)
1248 1249 1250 1251 1252 1253
      return selection_domain.asSQLExpression(
          strict_membership = strict_membership,
          join_table=join_table,
          join_column=join_column,
          base_category=base_category,
          category_table_alias = category_table_alias)
1254

1255 1256
    def _aq_dynamic(self, name):
      """
1257
        Generate viewSearchRelatedDocumentDialog0,
1258
                 viewSearchRelatedDocumentDialog1,... if necessary
1259 1260 1261
      """
      aq_base_name = getattr(aq_base(self), name, None)
      if aq_base_name == None:
1262 1263 1264
        DYNAMIC_METHOD_NAME = 'viewSearchRelatedDocumentDialog'
        method_name_length = len(DYNAMIC_METHOD_NAME)

1265
        zope_security = '__roles__'
1266
        if (name[:method_name_length] == DYNAMIC_METHOD_NAME) and \
1267
           (name[-len(zope_security):] != zope_security):
1268
          method_count_string_list = name[method_name_length:].split('_')
1269 1270 1271 1272
          method_count_string = method_count_string_list[0]
          # be sure that method name is correct
          try:
            method_count = string.atoi(method_count_string)
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1273
          except TypeError:
1274
            return aq_base_name
1275
          else:
1276 1277 1278
            if len(method_count_string_list) > 1:
              # be sure that method name is correct
              try:
Romain Courteaud's avatar
Romain Courteaud committed
1279
                sub_index = string.atoi(method_count_string_list[1])
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1280
              except TypeError:
1281 1282
                return aq_base_name
            else:
Romain Courteaud's avatar
Romain Courteaud committed
1283
              sub_index = None
1284

1285
            # generate dynamicaly needed forwarder methods
1286
            def viewSearchRelatedDocumentDialogWrapper(self, form_id,
1287
                                                       REQUEST=None, **kw):
1288 1289 1290
              """
                viewSearchRelatedDocumentDialog Wrapper
              """
1291 1292
#               LOG('SelectionTool.viewSearchRelatedDocumentDialogWrapper, kw',
#                   0, kw)
1293
              return self.viewSearchRelatedDocumentDialog(
1294
                                   method_count, form_id,
1295
                                   REQUEST=REQUEST, sub_index=sub_index, **kw)
1296
            setattr(self.__class__, name,
1297
                    viewSearchRelatedDocumentDialogWrapper)
1298 1299

            klass = aq_base(self).__class__
1300 1301 1302 1303
            security_property_id = '%s__roles__' % (name, )
            # Declare method as public
            setattr(klass, security_property_id, None)

1304 1305 1306 1307
            return getattr(self, name)
        else:
          return aq_base_name
      return aq_base_name
1308

1309 1310
    def _getUserId(self):
      return self.portal_membership.getAuthenticatedMember().getUserName()
1311 1312
      # XXX It would be good to add somthing here
      # So that 2 anonymous users do not share the same selection
1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331

    def _getSelectionFromContainer(self, selection_name):
      user_id = self._getUserId()
      if user_id is None: return None
      if self.isMemcachedUsed():
        return self._getMemcachedContainer().get('%s-%s' %
                                                 (user_id, selection_name))
      else:
        return self._getPersistentContainer(user_id).get(selection_name,
                                                         None)

    def _setSelectionToContainer(self, selection_name, selection):
      user_id = self._getUserId()
      if user_id is None: return
      if self.isMemcachedUsed():
        self._getMemcachedContainer().set('%s-%s' % (user_id, selection_name), aq_base(selection))
      else:
        self._getPersistentContainer(user_id)[selection_name] = aq_base(selection)

1332 1333 1334 1335 1336 1337 1338 1339
    def _deleteSelectionFromContainer(self, selection_name):
      user_id = self._getUserId()
      if user_id is None: return None
      if self.isMemcachedUsed():
        del(self._getMemcachedContainer()['%s-%s' % (user_id, selection_name)])
      else:
        del(self._getPersistentContainer(user_id)[selection_name])

1340
    def _deleteGlobalSelectionFromContainer(self, selection_name):
1341
      if not self.isMemcachedUsed():
1342 1343 1344 1345 1346 1347
        if getattr(aq_base(self), 'selection_data', None) is not None:
          for user_id in self.selection_data.keys():
            mapping = self._getPersistentContainer(user_id)
            if mapping.has_key(selection_name):
              del(mapping[selection_name])

1348 1349 1350 1351 1352 1353 1354 1355 1356
    def _getSelectionNameListFromContainer(self):
      if self.isMemcachedUsed():
        return []
      else:
        user_id = self._getUserId()
        if user_id is None: return []
        return self._getPersistentContainer(user_id).keys()

    def _getMemcachedContainer(self):
1357
      value = getattr(aq_base(self), '_v_selection_data', None)
1358
      if value is None:
1359 1360 1361 1362
        plugin_path = self.getStorage()
        value = self.getPortalObject().\
                portal_memcached.getMemcachedDict(key_prefix='selection_tool',
                                                  plugin_path=plugin_path)
1363 1364 1365 1366
        setattr(self, '_v_selection_data', value)
      return value

    def _getPersistentContainer(self, user_id):
1367
      if getattr(aq_base(self), 'selection_data', None) is None:
1368 1369 1370 1371 1372
        self.selection_data = PersistentMapping()
      if not self.selection_data.has_key(user_id):
        self.selection_data[user_id] = SelectionPersistentMapping()
      return self.selection_data[user_id]

Jean-Paul Smets's avatar
Jean-Paul Smets committed
1373
InitializeClass( SelectionTool )
1374

1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388

class SelectionPersistentMapping(PersistentMapping):
  """A conflict-free PersistentMapping.

  Like selection objects, the purpose is to only prevent restarting
  transactions.
  """
  def _p_independent(self) :
    return 1

  def _p_resolveConflict(self, oldState, savedState, newState):
    # update keys that only savedState has
    oldState = newState
    # dict returned by PersistentMapping.__getstate__ contains the data
1389
    # under '_container' key in zope 2.7 and 'data' in zope 2.8
Kazuhiko Shiozaki's avatar
typo.  
Kazuhiko Shiozaki committed
1390
    if 'data' in oldState:
1391 1392 1393
      oldState['data'].update(savedState['data'])
    else:
      oldState['_container'].update(savedState['_container'])
1394 1395 1396
    return oldState


1397 1398 1399 1400 1401 1402 1403 1404
class TreeListLine:
  def __init__(self,object,is_pure_summary,depth, is_open,select_domain_dict,exception_uid_list):
    self.object=object
    self.is_pure_summary=is_pure_summary
    self.depth=depth
    self.is_open=is_open
    self.select_domain_dict=select_domain_dict
    self.exception_uid_list=exception_uid_list
1405

1406 1407
  def getObject(self):
    return self.object
1408

1409 1410
  def getIsPureSummary(self):
    return self.is_pure_summary
1411

1412 1413
  def getDepth(self):
    return self.depth
1414

1415 1416
  def getIsOpen(self):
    return self.is_open
1417 1418

  def getSelectDomainDict(self):
1419
    return self.select_domain_dict
1420

1421 1422
  def getExceptionUidList(self):
    return self.exception_uid_list
1423

1424

1425
def makeTreeList(here, form, root_dict, report_path, base_category,
Nicolas Delaby's avatar
Nicolas Delaby committed
1426 1427 1428
                 depth, unfolded_list, form_id, selection_name,
                 report_depth, is_report_opened=1, list_method=None,
                 filtered_portal_types=[] ,sort_on = (('id', 'ASC'),)):
1429 1430 1431 1432 1433
  """
    (object, is_pure_summary, depth, is_open, select_domain_dict)

    select_domain_dict is a dictionary of  associative list of (id, domain)
  """
1434 1435
  if isinstance(report_path, str):
    report_path = report_path.split('/')
1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450

  portal_categories = getattr(form, 'portal_categories', None)
  portal_domains = getattr(form, 'portal_domains', None)
  portal_object = form.portal_url.getPortalObject()
  if len(report_path):
    base_category = report_path[0]

  if root_dict is None:
    root_dict = {}

  is_empty_level = 1
  while is_empty_level:
    if not root_dict.has_key(base_category):
      root = None
      if portal_categories is not None:
1451
        if portal_categories._getOb(base_category, None) is not None:
1452 1453 1454 1455
          if base_category == 'parent':
            # parent has a special treatment
            root = root_dict[base_category] = root_dict[None] = here
            report_path = report_path[1:]
1456
          else:
1457 1458
            root = root_dict[base_category] = root_dict[None] = \
                                               portal_categories[base_category]
1459 1460
            report_path = report_path[1:]
      if root is None and portal_domains is not None:
1461 1462 1463
        if portal_domains._getOb(base_category, None) is not None:
          root = root_dict[base_category] = root_dict[None] = \
                                               portal_domains[base_category]
1464 1465 1466
          report_path = report_path[1:]
      if root is None:
        try:
1467 1468
          root = root_dict[None] = \
              portal_object.unrestrictedTraverse(report_path)
1469
        except KeyError:
1470
          LOG('SelectionTool', INFO, "Not found %s" % str(report_path))
1471 1472 1473 1474 1475
          root = None
        report_path = ()
    else:
      root = root_dict[None] = root_dict[base_category]
      report_path = report_path[1:]
1476 1477
    is_empty_level = (root is not None) and \
        (root.objectCount() == 0) and (len(report_path) != 0)
1478
    if is_empty_level:
1479
      base_category = report_path[0]
1480 1481

  tree_list = []
1482
  if root is None:
1483
    return tree_list
1484

1485
  if base_category == 'parent':
1486 1487 1488 1489 1490
    # Use searchFolder as default
    if list_method is None:
      if hasattr(aq_base(root), 'objectValues'):
        # If this is a folder, try to browse the hierarchy
        object_list = root.searchFolder(sort_on=sort_on)
1491
    else:
1492
      if filtered_portal_types not in [[],None,'']:
1493 1494
        object_list = list_method(portal_type=filtered_portal_types,
                                  sort_on=sort_on)
1495
      else:
1496
        object_list = list_method(sort_on=sort_on)
1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512
    for zo in object_list:
      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

          for sub_zo in o.searchFolder(sort_on=sort_on):
            sub_o = sub_zo.getObject()
            if sub_o is not None and hasattr(aq_base(root), 'objectValues'):
              exception_uid_list.append(sub_o.getUid())
          # Summary (open)
1513
          tree_list += [TreeListLine(o, 1, depth, 1, selection_domain, exception_uid_list)]
1514 1515
          if is_report_opened :
            # List (contents, closed, must be strict selection)
1516 1517 1518
            tree_list += [TreeListLine(o, 0, depth, 0, selection_domain, exception_uid_list)]

          tree_list += makeTreeList(here, form, new_root_dict, report_path,
1519
      		    base_category, depth + 1, unfolded_list, form_id,
1520
      		    selection_name, report_depth,
1521 1522 1523
      		    is_report_opened=is_report_opened, sort_on=sort_on)
        else:
          tree_list += [TreeListLine(o, 1, depth, 0, selection_domain, ())] # Summary (closed)
1524
  else:
1525 1526 1527 1528 1529 1530
    # process to recover objects in case a generation script is used
    if hasattr(root,'getChildDomainValueList'):
      oblist = root.getChildDomainValueList(root,depth=depth)
    else:
      oblist = root.objectValues()
    for o in oblist:
1531 1532 1533 1534 1535 1536 1537
      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:
        tree_list += [TreeListLine(o, 1, depth, 1, selection_domain, None)] # Summary (open)
        if is_report_opened :
          tree_list += [TreeListLine(o, 0, depth, 0, selection_domain, None)] # List (contents, closed, must be strict selection)
1538 1539
        tree_list += makeTreeList(here, form, new_root_dict, report_path, base_category, depth + 1,
            unfolded_list, form_id, selection_name, report_depth,
1540 1541 1542 1543
            is_report_opened=is_report_opened, sort_on=sort_on)
      else:

        tree_list += [TreeListLine(o, 1, depth, 0, selection_domain, None)] # Summary (closed)
1544

1545 1546
  return tree_list

1547
# Automaticaly add wrappers on Folder so it can access portal_selections.
1548
# Cannot be done in ERP5Type/Document/Folder.py because ERP5Type must not
1549
# depend on ERP5Form.
1550 1551

from Products.CMFCore.utils import getToolByName
1552
from Products.ERP5Type.Core.Folder import FolderMixIn
1553 1554
from ZPublisher.mapply import mapply

1555 1556
method_id_filter_list = [x for x in FolderMixIn.__dict__ if callable(getattr(FolderMixIn, x))]
candidate_method_id_list = [x for x in SelectionTool.__dict__ if callable(getattr(SelectionTool, x)) and not x.startswith('_') and not x.endswith('__roles__') and x not in method_id_filter_list]
1557

1558 1559 1560
# Monkey patch FolderMixIn with SelectionTool methods
#   kept here for compatibility with previous implementations
#   of Listbox HTML renderer. See bellow new implementation
1561 1562 1563 1564 1565 1566 1567 1568 1569 1570
for property_id in candidate_method_id_list:
  def portal_selection_wrapper(self, wrapper_property_id=property_id, *args, **kw):
    """
      Wrapper method for SelectionTool.
    """
    portal_selection = getToolByName(self, 'portal_selections')
    request = self.REQUEST
    method = getattr(portal_selection, wrapper_property_id)
    return mapply(method, positional=args, keyword=request,
                  context=self, bind=1)
1571
  setattr(FolderMixIn, property_id, portal_selection_wrapper)
1572 1573 1574
  security_property_id = '%s__roles__' % (property_id, )
  security_property = getattr(SelectionTool, security_property_id, None)
  if security_property is not None:
1575
    setattr(FolderMixIn, security_property_id, security_property)
1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604

def createFolderMixInPageSelectionMethod(listbox_id):
  """
  This method must be called by listbox at rendering time.
  It dynamically creates methods on FolderMixIn in line
  with the naming of the listbox field. Generated method
  are able to convert request parameters in order to
  mimic the API of a listbox with ID "listbox". This
  approach was required for example to implement
  multiple multi-page listboxes in view mode. It also
  opens the way towards multiple editable listboxes in the same
  page although this is something which we can not recommend.
  """
  # Immediately return in the method already exists
  test_method_id = "%s_nextPage" % listbox_id
  if hasattr(FolderMixIn, test_method_id):
    return
  # Monkey patch FolderMixIn
  for property_id in candidate_method_id_list:
    def portal_selection_wrapper(self, wrapper_listbox_id=listbox_id,
                                       wrapper_property_id=property_id, *args, **kw):
      """
        Wrapper method for SelectionTool.
      """
      portal_selection = getToolByName(self, 'portal_selections')
      request = self.REQUEST
      selection_name_property_id = "%s_list_selection_name" % listbox_id
      listbox_uid_property_id = "%s_uid" % listbox_id
      list_start_property_id = "%s_list_start" % listbox_id
Kazuhiko Shiozaki's avatar
Kazuhiko Shiozaki committed
1605
      page_start_property_id = "%s_page_start" % listbox_id
1606 1607
      # Rename request parameters
      if request.has_key(selection_name_property_id):
1608
        request.form['list_selection_name'] = request[selection_name_property_id]
1609 1610 1611 1612
      if request.has_key(listbox_uid_property_id):
        request.form['listbox_uid'] = request[listbox_uid_property_id]
      if request.has_key(list_start_property_id):
        request.form['list_start'] = request[list_start_property_id]
Kazuhiko Shiozaki's avatar
Kazuhiko Shiozaki committed
1613 1614
      if request.has_key(page_start_property_id):
        request.form['page_start'] = request[page_start_property_id]
1615 1616 1617 1618 1619 1620 1621 1622 1623 1624
      # Call the wrapper
      method = getattr(portal_selection, wrapper_property_id)
      return mapply(method, positional=args, keyword=request,
                    context=self, bind=1)
    new_property_id = "%s_%s" % (listbox_id, property_id)
    setattr(FolderMixIn, new_property_id, portal_selection_wrapper)
    security_property_id = '%s__roles__' % (property_id, )
    security_property = getattr(SelectionTool, security_property_id, None)
    if security_property is not None:
      new_security_property_id = '%s__roles__' % (new_property_id, )
1625
      setattr(FolderMixIn, new_security_property_id, security_property)