SelectionTool.py 53.2 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 29
#
# 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.
#
##############################################################################

"""\
30
ERP portal_selection tool.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
31 32
"""

33
from OFS.Traversable import NotFound
Jean-Paul Smets's avatar
Jean-Paul Smets committed
34 35 36
from OFS.SimpleItem import SimpleItem
from Products.CMFCore.utils import UniqueObject
from Globals import InitializeClass, DTMLFile, PersistentMapping, get_request
37
from ZTUtils import make_query
Jean-Paul Smets's avatar
Jean-Paul Smets committed
38 39 40
from AccessControl import ClassSecurityInfo
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 44
from email.MIMEBase import MIMEBase
from email import Encoders
Sebastien Robin's avatar
Sebastien Robin committed
45
from copy import copy
46
from DateTime import DateTime
Sebastien Robin's avatar
Sebastien Robin committed
47
import md5
Sebastien Robin's avatar
Sebastien Robin committed
48 49 50
import pickle
import hmac
import random
51
import re
52
import string
Jean-Paul Smets's avatar
Jean-Paul Smets committed
53
from zLOG import LOG
54
from Acquisition import Implicit, aq_base
55
from Products.ERP5Type.Message import Message
56

Jean-Paul Smets's avatar
Jean-Paul Smets committed
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
class SelectionError( Exception ):
    pass

class SelectionTool( UniqueObject, SimpleItem ):
    """
      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'

    security = ClassSecurityInfo()

    #
    #   ZMI methods
    #
    manage_options = ( ( { 'label'      : 'Overview'
                         , 'action'     : 'manage_overview'
77 78 79 80
                         },
                         { 'label'      : 'View Selections'
                         , 'action'     : 'manage_view_selections'
                         } ))
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 87 88
    security.declareProtected( ERP5Permissions.ManagePortal
                             , 'manage_view_selections' )
    manage_view_selections = DTMLFile( 'SelectionTool_manageViewSelections', _dtmldir )
89 90 91 92 93 94 95 96 97 98 99 100 101

    _query_string_reset_regexp = re.compile('\\breset(:int|)?=')
    _query_string_report_depth_regexp = re.compile('\\breport_depth(:int|)?=')
    def _redirectToOriginalForm(self, REQUEST=None, form_id=None, query_string=None,
                                no_reset=False, no_report_depth=False):
      """Redirect to the original form, using the information given as parameters.
         If no_reset is True, replace reset parameters with noreset.
         If no_report_depth is True, replace report_depth parameters with noreport_depth.
      """
      if REQUEST is None:
        return

      context = self.aq_parent
102
      form_id = form_id or REQUEST.get('form_id', 'view')
103 104 105 106 107 108 109 110 111 112 113
      url = context.absolute_url() + '/' + form_id
      if query_string is not None:
        if no_reset:
          query_string = self._query_string_reset_regexp.sub('noreset\\1=',
                                                             query_string)
        if no_report_depth:
          query_string = self._query_string_report_depth_regexp.sub('noreport_depth\\1=',
                                                                    query_string)
        url = url + '?' + query_string
      return REQUEST.RESPONSE.redirect(url)

114 115 116 117 118 119 120 121 122 123 124
    security.declareProtected(ERP5Permissions.View, 'getSelectionNames')
    def getSelectionNames(self, context=None, REQUEST=None):
      if context is None: context = self
      if not REQUEST:
        REQUEST = get_request()
        if hasattr(self, 'selection_data'):
          user_id = self.portal_membership.getAuthenticatedMember().getUserName()
        if user_id is not None and self.selection_data.has_key(user_id):
          return self.selection_data[user_id].keys()
      return ()

125 126 127 128 129 130 131 132
    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
133 134 135 136 137 138 139
    security.declareProtected(ERP5Permissions.View, 'getSelectionFor')
    def getSelectionFor(self, selection_name, REQUEST=None):
      """
        Returns the selection instance for a given selection_name
      """
      if not REQUEST:
        REQUEST = get_request()
140

141 142 143 144 145 146
      if not hasattr(self, 'selection_data'):
        self.selection_data = PersistentMapping()
      user_id = self.portal_membership.getAuthenticatedMember().getUserName()
      if user_id is not None:
        if not self.selection_data.has_key(user_id):
          self.selection_data[user_id] = PersistentMapping()
147 148
        if type(selection_name) is type(()) or type(selection_name) is type([]) :
          selection_name = selection_name[0]
149 150
        return self.selection_data[user_id].get(selection_name, None)
      else:
Jean-Paul Smets's avatar
Jean-Paul Smets committed
151
        return None
152

Jean-Paul Smets's avatar
Jean-Paul Smets committed
153 154 155 156 157
    security.declareProtected(ERP5Permissions.View, 'setSelectionFor')
    def setSelectionFor(self, selection_name, selection_object, REQUEST=None):
      """
        Sets the selection instance for a given selection_name
      """
158 159 160
      if selection_object != None:
        # Set the name so that this selection itself can get its own name.
        selection_object.edit(name = selection_name)
161

Jean-Paul Smets's avatar
Jean-Paul Smets committed
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
      if not REQUEST:
        REQUEST = get_request()
      # New system: store directly - bypass session
      if 0:
        user_id = self.portal_membership.getAuthenticatedMember().getUserName()
        if user_id is not None:
          self.selection_data.set((user_id, selection_name), selection_object)
        return

      # Another method: local dict
      if 0:
        if not hasattr(self, 'selection_data'):
          self.selection_data = PersistentMapping()
        user_id = self.portal_membership.getAuthenticatedMember().getUserName()
        if user_id is not None:
          self.selection_data[(user_id, selection_name)] = selection_object
        return

      # Another method: local dict but 2 stage to prevent user conflict
      if 1:
        if not hasattr(self, 'selection_data'):
          self.selection_data = PersistentMapping()
        user_id = self.portal_membership.getAuthenticatedMember().getUserName()
        if user_id is not None:
          if not self.selection_data.has_key(user_id):
            self.selection_data[user_id] = PersistentMapping()
          self.selection_data[user_id][selection_name] = selection_object
        return

      #try: CAUSES PROBLEMS WHY ??
      if 1:
        session = REQUEST.SESSION
        selection_name = selection_name + '_selection_object'
        session[selection_name] = selection_object
      #except:
      #  LOG('WARNING ERP5Form SelectionTool',0,'Could not set Selection')

199 200
    security.declareProtected(ERP5Permissions.View, 'getSelectionParamsFor')
    def getSelectionParamsFor(self, selection_name, params=None, REQUEST=None):
201 202 203
      """
        Returns the params in the selection
      """
204
      if params is None: params = {}
205 206
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
207 208
        if len(selection.params) > 0:
          return selection.getParams()
209 210 211 212
        else:
          return params
      else:
        return params
213 214 215 216 217
    
    # backward compatibility 
    security.declareProtected(ERP5Permissions.View, 'getSelectionParams')
    getSelectionParams = getSelectionParamsFor
    
Jean-Paul Smets's avatar
Jean-Paul Smets committed
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
    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)
      if selection_object:
        selection_object.edit(params=params)
      else:
        selection_object = Selection(params=params)
      self.setSelectionFor(selection_name, selection_object, REQUEST)

    security.declareProtected(ERP5Permissions.View, 'setSelectionCheckedUidsFor')
    def setSelectionCheckedUidsFor(self, selection_name, checked_uids, REQUEST=None):
      """
        Sets the selection params for a given selection_name
      """
      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)

242 243 244 245 246 247 248 249 250 251 252
    def updateSelectionCheckedUidList(self, selection_name, listbox_uid, uids, REQUEST=None):
      """
        Sets the selection params for a given selection_name
      """
      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)

253 254 255 256 257 258 259
    security.declareProtected(ERP5Permissions.View, 'getSelectionCheckedUidsFor')
    def getSelectionCheckedUidsFor(self, selection_name, REQUEST=None):
      """
        Sets the selection params for a given selection_name
      """
      selection_object = self.getSelectionFor(selection_name, REQUEST)
      if selection_object:
260
        #return selection_object.selection_checked_uids
261
        return selection_object.getCheckedUids()
262 263 264
      return []

    security.declareProtected(ERP5Permissions.View, 'checkAll')
265 266
    def checkAll(self, selection_name, listbox_uid=[], REQUEST=None,
                 query_string=None, form_id=None):
267 268 269 270 271 272
      """
        Sets the selection params for a given selection_name
      """
      selection_object = self.getSelectionFor(selection_name, REQUEST)
      if selection_object:
        selection_uid_dict = {}
273
        for uid in selection_object.checked_uids:
274 275
          selection_uid_dict[uid] = 1
        for uid in listbox_uid:
276 277
          try:
            selection_uid_dict[int(uid)] = 1
278
          except ValueError:
279
            selection_uid_dict[uid] = 1
280
        self.setSelectionCheckedUidsFor(selection_name, selection_uid_dict.keys(), REQUEST=REQUEST)
281 282 283
      if REQUEST is not None:
        return self._redirectToOriginalForm(REQUEST=REQUEST, form_id=form_id,
                                            query_string=query_string, no_reset=True)
284 285

    security.declareProtected(ERP5Permissions.View, 'uncheckAll')
286 287
    def uncheckAll(self, selection_name, listbox_uid=[], REQUEST=None,
                   query_string=None, form_id=None):
288 289 290 291 292 293
      """
        Sets the selection params for a given selection_name
      """
      selection_object = self.getSelectionFor(selection_name, REQUEST)
      if selection_object:
        selection_uid_dict = {}
294
        for uid in selection_object.checked_uids:
295 296
          selection_uid_dict[uid] = 1
        for uid in listbox_uid:
297 298 299
          try:
            if selection_uid_dict.has_key(int(uid)): del selection_uid_dict[int(uid)]
          except ValueError:
300
            if selection_uid_dict.has_key(uid): del selection_uid_dict[uid]
301
        self.setSelectionCheckedUidsFor(selection_name, selection_uid_dict.keys(), REQUEST=REQUEST)
302 303 304
      if REQUEST is not None:
        return self._redirectToOriginalForm(REQUEST=REQUEST, form_id=form_id,
                                            query_string=query_string, no_reset=True)
305

Jean-Paul Smets's avatar
Jean-Paul Smets committed
306 307 308 309 310 311 312
    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:
313
        return selection.getListUrl()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
314 315 316 317 318 319 320 321 322 323
      else:
        return None

    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:
324
        selection.edit(invert_mode=1, uids=selection_uids, checked_uids=selection_uids)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
325 326

    security.declareProtected(ERP5Permissions.View, 'setSelectionToAll')
327 328
    def setSelectionToAll(self, selection_name, REQUEST=None,
                          reset_domain_tree=False, reset_report_tree=False):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
329 330 331 332 333
      """
        Resets the selection
      """
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
334
        selection.edit(invert_mode=0, params={}, checked_uids=[], report_opened=1)
335
        if reset_domain_tree:
336
          selection.edit(domain=None, domain_path=None, domain_list=None)
337
        if reset_report_tree:
338
          selection.edit(report=None, report_path=None, report_list=None)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
339 340 341 342 343 344 345 346 347 348 349

    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')
350 351
    def setSelectionQuickSortOrder(self, selection_name, sort_on, REQUEST=None,
                                   query_string=None, form_id=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
      """
        Defines the sort order of the selection directly from the listbox
        In this method, sort_on is just a string that comes from url
      """
      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)

376 377 378
      if REQUEST is not None:
        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
379 380 381 382 383 384 385 386

    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 ()
387
      return selection.sort_on
Jean-Paul Smets's avatar
Jean-Paul Smets committed
388 389 390 391 392 393 394 395 396 397

    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')
398
    def getSelectionColumns(self, selection_name, columns=None, REQUEST=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
399 400 401
      """
        Returns the columns in the selection
      """
402
      if columns is None: columns = []
Jean-Paul Smets's avatar
Jean-Paul Smets committed
403 404
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
405 406
        if len(selection.columns) > 0:
          return selection.columns
407
      return columns
Jean-Paul Smets's avatar
Jean-Paul Smets committed
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425


    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')
    def getSelectionStats(self, selection_name, stats=[' ',' ',' ',' ',' ',' '], REQUEST=None):
      """
        Returns the stats in the selection
      """
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
        try:
426
          return selection.stats
Yoshinori Okuji's avatar
Yoshinori Okuji committed
427
        except AttributeError:
428
          return stats # That is really bad programming XXX
Jean-Paul Smets's avatar
Jean-Paul Smets committed
429 430 431 432 433 434 435 436 437 438 439 440 441
      else:
        return stats


    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:
442
        method = self.unrestrictedTraverse(selection.method_path)
443 444
        selection_list = selection(method = method, context=self, REQUEST=REQUEST)
        o = selection_list[0]
Jean-Paul Smets's avatar
Jean-Paul Smets committed
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
        url = o.absolute_url()
      else:
        url = REQUEST.url
      url = '%s/%s?selection_index=%s&selection_name=%s' % (url, form_id, 0, selection_name)
      REQUEST.RESPONSE.redirect(url)

    security.declareProtected(ERP5Permissions.View, 'viewLast')
    def viewLast(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:
460
        method = self.unrestrictedTraverse(selection.method_path)
461 462
        selection_list = selection(method = method, context=self, REQUEST=REQUEST)
        o = selection_list[-1]
Jean-Paul Smets's avatar
Jean-Paul Smets committed
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
        url = o.absolute_url()
      else:
        url = REQUEST.url
      url = '%s/%s?selection_index=%s&selection_name=%s' % (url, form_id, -1, selection_name)
      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:
478
        method = self.unrestrictedTraverse(selection.method_path)
479 480
        selection_list = selection(method = method, context=self, REQUEST=REQUEST)
        o = selection_list[(int(selection_index) + 1) % len(selection_list)]
Jean-Paul Smets's avatar
Jean-Paul Smets committed
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495
        url = o.absolute_url()
      else:
        url = REQUEST.url
      url = '%s/%s?selection_index=%s&selection_name=%s' % (url, form_id, int(selection_index) + 1, selection_name)
      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:
496
        method = self.unrestrictedTraverse(selection.method_path)
497 498
        selection_list = selection(method = method, context=self, REQUEST=REQUEST)
        o = selection_list[(int(selection_index) - 1) % len(selection_list)]
Jean-Paul Smets's avatar
Jean-Paul Smets committed
499 500 501 502 503 504 505 506 507
        url = o.absolute_url()
      else:
        url = REQUEST.url
      url = '%s/%s?selection_index=%s&selection_name=%s' % (url, form_id, int(selection_index) - 1, selection_name)
      REQUEST.RESPONSE.redirect(url)


    # ListBox related methods
    security.declareProtected(ERP5Permissions.View, 'nextPage')
508
    def nextPage(self, listbox_uid, uids=None, REQUEST=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
509 510 511
      """
        Access the next page of a list
      """
512
      if uids is None: uids = []
Jean-Paul Smets's avatar
Jean-Paul Smets committed
513
      request = REQUEST
514
      #form_id = request.form_id
Jean-Paul Smets's avatar
Jean-Paul Smets committed
515 516
      selection_name = request.list_selection_name
      selection = self.getSelectionFor(selection_name, REQUEST)
517
      params = selection.getParams()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
518 519 520 521 522
      lines = params.get('list_lines',0)
      start = params.get('list_start', 0)
      params['list_start'] = int(start) + int(lines)
      selection.edit(params= params)

523 524
      self.uncheckAll(selection_name, listbox_uid)
      return self.checkAll(selection_name, uids, REQUEST=REQUEST)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
525 526

    security.declareProtected(ERP5Permissions.View, 'previousPage')
527
    def previousPage(self, listbox_uid, uids=None, REQUEST=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
528 529 530
      """
        Access the previous page of a list
      """
531
      if uids is None: uids = []
Jean-Paul Smets's avatar
Jean-Paul Smets committed
532
      request = REQUEST
533
      #form_id = request.form_id
Jean-Paul Smets's avatar
Jean-Paul Smets committed
534 535
      selection_name = request.list_selection_name
      selection = self.getSelectionFor(selection_name, REQUEST)
536
      params = selection.getParams()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
537 538 539
      lines = params.get('list_lines',0)
      start = params.get('list_start', 0)
      params['list_start'] = max(int(start) - int(lines), 0)
540
      selection.edit(params= selection.params)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
541

542 543
      self.uncheckAll(selection_name, listbox_uid)
      return self.checkAll(selection_name, uids, REQUEST=REQUEST)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
544 545

    security.declareProtected(ERP5Permissions.View, 'setPage')
546
    def setPage(self, listbox_uid, query_string=None, uids=None, REQUEST=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
547 548 549
      """
        Access the previous page of a list
      """
550
      if uids is None: uids = []
Jean-Paul Smets's avatar
Jean-Paul Smets committed
551
      request = REQUEST
552
      #form_id = request.form_id
Jean-Paul Smets's avatar
Jean-Paul Smets committed
553
      selection_name = request.list_selection_name
554

Jean-Paul Smets's avatar
Jean-Paul Smets committed
555 556
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
557
        params = selection.getParams()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
558 559 560 561
        lines = params.get('list_lines',0)
        start = request.form.get('list_start',0)

        params['list_start'] = start
562
        selection.edit(params= selection.params)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
563

564
      self.uncheckAll(selection_name, listbox_uid)
565 566
      return self.checkAll(selection_name, uids, REQUEST=REQUEST, query_string=query_string)

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

568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
    # PlanningBox related methods
    security.declareProtected(ERP5Permissions.View, 'setZoomLevel')
    def setZoomLevel(self, uids=None, REQUEST=None):
      """
      Set graphic zoom level in PlanningBox
      """
      if uids is None: uids = []
      request = REQUEST
      #zoom_level=request.get('zoom_level')
      selection_name=request.list_selection_name
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
        params = selection.getParams()
        zoom_level = request.form.get('zoom_level',1)
        zoom_start = request.form.get('zoom_start',0)
        params['zoom_level'] = zoom_level
        if zoom_level <= zoom_start:
          zoom_start = max(int(float(zoom_level)),1) - 1
          params['zoom_start'] = zoom_start
        selection.edit(params= params)
      if REQUEST is not None:
        return self._redirectToOriginalForm(REQUEST=REQUEST)

591
    security.declareProtected(ERP5Permissions.View, 'setZoom')
592
    def setZoom(self, uids=None, REQUEST=None):
593 594
      """
      Set graphic zoom in PlanningBox
595
      """
596
      if uids is None: uids = []
597
      request = REQUEST
598 599 600 601
      selection_name=request.list_selection_name
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
        params = selection.getParams()
602 603
        zoom_start = request.form.get('zoom_start')
        params['zoom_start'] = zoom_start
604
        selection.edit(params= params)
605
      if REQUEST is not None:
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
        return self._redirectToOriginalForm(REQUEST=REQUEST)

    security.declareProtected(ERP5Permissions.View, 'nextZoom')
    def nextZoom(self, uids=None, REQUEST=None):
      """
      Set next graphic zoom start in PlanningBox
      """
      if uids is None: uids = []
      request = REQUEST
      selection_name=request.list_selection_name
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
        params = selection.getParams()
        zoom_start = params.get('zoom_start')
        params['zoom_start'] = int(zoom_start) + 1
        selection.edit(params= params)
      if REQUEST is not None:
        return self._redirectToOriginalForm(REQUEST=REQUEST)

    security.declareProtected(ERP5Permissions.View, 'previousZoom')
    def previousZoom(self, uids=None, REQUEST=None):
      """
      Set previous graphic zoom in PlanningBox
      """
      if uids is None: uids = []
      request = REQUEST
      selection_name=request.list_selection_name
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
        params = selection.getParams()
        zoom_start = params.get('zoom_start')
        params['zoom_start'] = int(zoom_start) - 1
        selection.edit(params= params)
      if REQUEST is not None:
        return self._redirectToOriginalForm(REQUEST=REQUEST)
641

Jean-Paul Smets's avatar
Jean-Paul Smets committed
642
    security.declareProtected(ERP5Permissions.View, 'setDomainRoot')
643
    def setDomainRoot(self, REQUEST, form_id=None, query_string=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
644 645 646
      """
        Sets the root domain for the current selection
      """
647
      selection_name = REQUEST.list_selection_name
Jean-Paul Smets's avatar
Jean-Paul Smets committed
648
      selection = self.getSelectionFor(selection_name, REQUEST)
649
      root_url = REQUEST.form.get('domain_root_url','portal_categories')
650
      selection.edit(domain_path=root_url, domain_list=())
Jean-Paul Smets's avatar
Jean-Paul Smets committed
651

652 653 654
      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
655

656
    security.declareProtected(ERP5Permissions.View, 'unfoldDomain')
657
    def unfoldDomain(self, REQUEST, form_id=None, query_string=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
658 659 660
      """
        Sets the root domain for the current selection
      """
661
      selection_name = REQUEST.list_selection_name
Jean-Paul Smets's avatar
Jean-Paul Smets committed
662
      selection = self.getSelectionFor(selection_name, REQUEST)
663 664
      domain_url = REQUEST.form.get('domain_url',None)
      domain_depth = REQUEST.form.get('domain_depth',0)
665 666
      domain_list = list(selection.getDomainList())
      domain_list = domain_list[0:min(domain_depth, len(domain_list))]
667
      if isinstance(domain_url, str):
668
        selection.edit(domain_list = domain_list + [domain_url])
Jean-Paul Smets's avatar
Jean-Paul Smets committed
669

670 671 672
      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
673

674
    security.declareProtected(ERP5Permissions.View, 'foldDomain')
675
    def foldDomain(self, REQUEST, form_id=None, query_string=None):
676 677 678
      """
        Sets the root domain for the current selection
      """
679
      selection_name = REQUEST.list_selection_name
680
      selection = self.getSelectionFor(selection_name, REQUEST)
681 682
      domain_url = REQUEST.form.get('domain_url',None)
      domain_depth = REQUEST.form.get('domain_depth',0)
683 684
      domain_list = list(selection.getDomainList())
      domain_list = domain_list[0:min(domain_depth, len(domain_list))]
685
      selection.edit(domain_list=[x for x in domain_list if x != domain_url])
686

687 688 689
      if REQUEST is not None:
        return self._redirectToOriginalForm(REQUEST=REQUEST, form_id=form_id,
                                            query_string=query_string)
690

691

Jean-Paul Smets's avatar
Jean-Paul Smets committed
692
    security.declareProtected(ERP5Permissions.View, 'setReportRoot')
693
    def setReportRoot(self, REQUEST, form_id=None, query_string=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
694 695 696
      """
        Sets the root domain for the current selection
      """
697
      selection_name = REQUEST.list_selection_name
Jean-Paul Smets's avatar
Jean-Paul Smets committed
698
      selection = self.getSelectionFor(selection_name, REQUEST)
699
      root_url = REQUEST.form.get('report_root_url','portal_categories')
700
      selection.edit(report_path=root_url, report_list=())
Jean-Paul Smets's avatar
Jean-Paul Smets committed
701

702 703 704
      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
705 706

    security.declareProtected(ERP5Permissions.View, 'unfoldReport')
707
    def unfoldReport(self, REQUEST, form_id=None, query_string=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
708 709
      """
        Sets the root domain for the current selection
710

711
        report_list is a list of relative_url of category, domain, etc.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
712
      """
713
      selection_name = REQUEST.list_selection_name
Jean-Paul Smets's avatar
Jean-Paul Smets committed
714
      selection = self.getSelectionFor(selection_name, REQUEST)
715
      report_url = REQUEST.form.get('report_url',None)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
716
      if type(report_url) == type('a'):
717
        selection.edit(report_list=list(selection.getReportList()) + [report_url])
Jean-Paul Smets's avatar
Jean-Paul Smets committed
718

719 720 721
      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
722 723

    security.declareProtected(ERP5Permissions.View, 'foldReport')
724
    def foldReport(self, REQUEST, form_id=None, query_string=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
725 726 727
      """
        Sets the root domain for the current selection
      """
728
      selection_name = REQUEST.list_selection_name
Jean-Paul Smets's avatar
Jean-Paul Smets committed
729
      selection = self.getSelectionFor(selection_name, REQUEST)
730
      report_url = REQUEST.form.get('report_url',None)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
731
      if type(report_url) == type('a'):
732
        report_list = selection.getReportList()
733
        selection.edit(report_list=[x for x in report_list if x != report_url])
Jean-Paul Smets's avatar
Jean-Paul Smets committed
734

735 736 737
      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
738

739 740 741 742
    security.declareProtected(ERP5Permissions.View, 'getListboxDisplayMode')
    def getListboxDisplayMode(self, selection_name, REQUEST=None):
      if REQUEST is None:
        REQUEST = get_request()
743
      selection = self.getSelectionFor(selection_name, REQUEST)
744

745 746 747 748 749
      if getattr(selection, 'report_tree_mode', 0):
        return 'ReportTreeMode'
      elif getattr(selection, 'domain_tree_mode', 0):
        return 'DomainTreeMode'
      return 'FlatListMode'
750

Jean-Paul Smets's avatar
Jean-Paul Smets committed
751
    security.declareProtected(ERP5Permissions.View, 'setListboxDisplayMode')
752
    def setListboxDisplayMode(self, REQUEST, listbox_display_mode, 
753 754
                              selection_name=None, redirect=0,
                              form_id=None, query_string=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
755 756 757 758
      """
        Toogle display of the listbox
      """
      request = REQUEST
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
      # 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
      # Possible fix: currently, display mode icon are implemented as 
      # method. It could be easier to generate them as link (where we 
      # 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
777
      selection = self.getSelectionFor(selection_name, REQUEST)
778 779
      if selection is None:
        selection = Selection()
780
        self.setSelectionFor(selection_name, selection, REQUEST=REQUEST)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
781 782 783 784 785 786 787 788 789 790 791 792 793

      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
794 795 796 797
      else:
        flat_list_mode = 0
        domain_tree_mode = 0
        report_tree_mode = 0      
798

799 800 801
      selection.edit(flat_list_mode=flat_list_mode,
                     domain_tree_mode=domain_tree_mode,
                     report_tree_mode=report_tree_mode)
802
      # It is better to reset the query when changing the display mode.
803 804
      params = selection.getParams()
      if 'where_expression' in params: del params['where_expression']
805 806
      selection.edit(params = params)

807
      if redirect:
808 809 810
        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
811 812

    security.declareProtected(ERP5Permissions.View, 'setFlatListMode')
813
    def setFlatListMode(self, REQUEST, selection_name=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
814 815 816
      """
        Set display of the listbox to FlatList mode
      """
817 818 819
      return self.setListboxDisplayMode(
                       REQUEST=REQUEST, listbox_display_mode='FlatListMode', 
                       selection_name=selection_name, redirect=1)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
820 821

    security.declareProtected(ERP5Permissions.View, 'setDomainTreeMode')
822
    def setDomainTreeMode(self, REQUEST, selection_name=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
823 824 825
      """
         Set display of the listbox to DomainTree mode
      """
826 827 828
      return self.setListboxDisplayMode(
                       REQUEST=REQUEST, listbox_display_mode='DomainTreeMode', 
                       selection_name=selection_name, redirect=1)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
829 830

    security.declareProtected(ERP5Permissions.View, 'setReportTreeMode')
831
    def setReportTreeMode(self, REQUEST, selection_name=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
832 833 834
      """
        Set display of the listbox to ReportTree mode
      """
835 836 837
      return self.setListboxDisplayMode(
                       REQUEST=REQUEST, listbox_display_mode='ReportTreeMode',
                       selection_name=selection_name, redirect=1)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
838

839 840 841 842 843 844
    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)
845 846
      if selection is None:
        return []
847
      return selection(method=selection_method, context=context, REQUEST=REQUEST)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
848

849 850 851 852 853 854
    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)
855 856
      if selection is None:
        return []
857
      uid_list = selection.getCheckedUids()
858 859 860 861 862 863 864 865 866 867
      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:
868 869 870 871 872
        value_list = self.getSelectionSelectedValueList(
                                            selection_name,
                                            REQUEST=REQUEST, 
                                            selection_method=selection_method, 
                                            context=context)
873
      return value_list
Jean-Paul Smets's avatar
Jean-Paul Smets committed
874

Jean-Paul Smets's avatar
Jean-Paul Smets committed
875 876 877 878 879 880 881
    security.declareProtected(ERP5Permissions.View, 'getSelectionUidList')
    def getSelectionUidList(self, selection_name, REQUEST=None, selection_method=None, context=None):
      """
        Get the list of values checked or selected for 'selection_name'
      """
      return map(lambda x:x.getObject().getUid(), self.getSelectionValueList(selection_name, REQUEST=REQUEST, selection_method=selection_method, context=context))

Sebastien Robin's avatar
Sebastien Robin committed
882 883 884 885 886
    security.declareProtected(ERP5Permissions.View, 'selectionHasChanged')
    def selectionHasChanged(self, md5_string, object_uid_list):
      """
        We want to be sure that the selection did not change
      """
887 888 889 890 891 892
      # XXX To avoid the difference of the string representations of int and long,
      # convert each element to a string. 
      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
893

894 895
    security.declareProtected(ERP5Permissions.View, 'getPickle')
    def getPickle(self,**kw):
Sebastien Robin's avatar
Sebastien Robin committed
896 897 898 899
      """
      we give many keywords and we will get the corresponding
      pickle string and signature
      """
Yoshinori Okuji's avatar
Yoshinori Okuji committed
900
      #LOG('getPickle kw',0,kw)
901 902 903
      # XXX Remove DateTime, This is really bad, only use for zope 2.6
      # XXX This has to be removed as quickly as possible
      for k,v in kw.items():
Sebastien Robin's avatar
Sebastien Robin committed
904
        if isinstance(v,DateTime):
905 906
          del kw[k]
      # XXX End of the part to remove
907
      #LOG('SelectionTool.getPickle, kw',0,kw)
Sebastien Robin's avatar
Sebastien Robin committed
908 909 910 911 912 913
      pickle_string = pickle.dumps(kw)
      msg = MIMEBase('application','octet-stream')
      msg.set_payload(pickle_string)
      Encoders.encode_base64(msg)
      pickle_string = msg.get_payload()
      pickle_string = pickle_string.replace('\n','@@@')
914 915 916 917 918 919 920 921
      return pickle_string

    security.declareProtected(ERP5Permissions.View, 'getPickleAndSignature')
    def getPickleAndSignature(self,**kw):
      """
      we give many keywords and we will get the corresponding
      pickle string and signature
      """
922
      cookie_password = self._getCookiePassword()
923
      pickle_string = self.getPickle(**kw)
Sebastien Robin's avatar
Sebastien Robin committed
924 925 926
      signature = hmac.new(cookie_password,pickle_string).hexdigest()
      return (pickle_string,signature)

927 928 929 930 931 932 933 934 935 936 937 938 939 940
    security.declareProtected(ERP5Permissions.View, 'getObjectFromPickle')
    def getObjectFromPickle(self,pickle_string):
      """
      we give a pickle string and a signature
      """
      object = None
      pickle_string = pickle_string.replace('@@@','\n')
      msg = MIMEBase('application','octet-stream')
      Encoders.encode_base64(msg)
      msg.set_payload(pickle_string)
      pickle_string = msg.get_payload(decode=1)
      object = pickle.loads(pickle_string)
      return object

Sebastien Robin's avatar
Sebastien Robin committed
941 942 943 944 945 946 947 948 949
    security.declareProtected(ERP5Permissions.View, 'getObjectFromPickleAndSignature')
    def getObjectFromPickleAndSignature(self,pickle_string,signature):
      """
      we give a pickle string and a signature
      """
      cookie_password = self._getCookiePassword()
      object = None
      new_signature = hmac.new(cookie_password,pickle_string).hexdigest()
      if new_signature==signature:
950
        object = self.getObjectFromPickle(pickle_string)
Sebastien Robin's avatar
Sebastien Robin committed
951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990
      return object

    security.declarePrivate('_getCookiePassword')
    def _getCookiePassword(self):
      """
      get the password used for encryption
      """
      cookie_password = getattr(self,'cookie_password',None)
      if cookie_password is None:
        cookie_password = str(random.randrange(1,2147483600))
        self.cookie_password = cookie_password
      return cookie_password

    security.declareProtected(ERP5Permissions.View, 'registerCookieInfo')
    def setCookieInfo(self,request,cookie_name,**kw):
      """
      regiter info directly in cookie
      """
      cookie_name = cookie_name + '_cookie'
      (pickle_string,signature) = self.getPickleAndSignature(**kw)
      request.RESPONSE.setCookie(cookie_name,pickle_string,max_age=15*60)
      signature_cookie_name = cookie_name + '_signature'
      request.RESPONSE.setCookie(signature_cookie_name,signature,max_age=15*60)

    security.declareProtected(ERP5Permissions.View, 'registerCookieInfo')
    def getCookieInfo(self,request,cookie_name):
      """
      regiter info directly in cookie
      """
      cookie_name = cookie_name + '_cookie'
      object = None
      if getattr(request,cookie_name,None) is not None:
        pickle_string = request.get(cookie_name)
        signature_cookie_name = cookie_name + '_signature'
        signature = request.get(signature_cookie_name)
        object = self.getObjectFromPickleAndSignature(pickle_string,signature)
      if object is None:
        object = {}
      return object

991
    # Related document searching
992
    def viewSearchRelatedDocumentDialog(self, index, form_id, REQUEST=None,
993
                                        sub_index=None, **kw):
994
      """
995 996
      Returns a search related document dialog
      A set of forwarders us defined to circumvent limitations of HTML
997
      """
Romain Courteaud's avatar
Romain Courteaud committed
998 999
      if sub_index != None:
        REQUEST.form['sub_index'] = sub_index
1000
      # Find the object which needs to be updated
1001
      object_uid = REQUEST.get('object_uid', None)
1002
      object_path = REQUEST.get('object_path', None)
1003
      if object_uid is not None:
1004 1005
        try :
          o = self.portal_catalog.getObject(object_uid)
1006
        except (NotFound, KeyError), err :
1007
          o = None
1008 1009 1010
      if o is None:
        # we first try to reindex the object, thanks to the object_path
        if object_path is not None:
1011
          o = self.getPortalObject().restrictedTraverse(object_path)
1012
        if o is not None:
1013
          # XXX
1014
          o.immediateReindexObject()
1015
          object_uid = o.getUid()
1016
        else:
1017 1018 1019
          raise SelectionError, \
                "Sorrry, Error, the calling object was not catalogued. " \
                "Do not know how to do ?"
1020
      # Find the field which was clicked on
1021
      # Important to get from the object instead of self
1022
      form = getattr(o, form_id)
1023
      field = None
1024 1025
      # Search the correct field
      relation_field_found = 0
1026
      relation_index = 0
1027
      # XXX may be should support another parameter,
1028
      for field in form.get_fields(include_disabled=0):
1029 1030
        if field.get_value('editable', REQUEST=REQUEST):
          try:
1031 1032
           field.get_value('is_relation_field')
          except KeyError:
1033
            pass
1034
          else:
1035 1036 1037 1038 1039
            if index == relation_index:
              relation_field_found = 1
              break
            else:
              relation_index += 1
1040
      if not relation_field_found:
1041
        # We didn't find the field...
1042
        raise SelectionError, "SelectionTool: can not find the relation" \
1043
                              " field %s" % index
1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058
      else:
        # Field found
        field_key = field.generate_field_key()
        field_value = REQUEST.form[field_key]
        # XXX Hardcoded form name
        redirect_form_id = 'Base_viewRelatedObjectList'
        redirect_form = getattr(o, redirect_form_id)
        # XXX Hardcoded listbox field
        selection_name = redirect_form.listbox.get_value('selection_name')
        # Reset current selection
        self.portal_selections.setSelectionFor(selection_name, None)


        if (field.get_value('is_multi_relation_field')) and \
           (sub_index is None):
1059 1060 1061 1062
          # user click on the wheel, not on the validation button
          # we need to facilitate user search

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

1065 1066 1067 1068 1069 1070
          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
1071 1072 1073
          # Checked current uid
          kw ={}
          kw[field.get_value('catalog_index')] = field_value
1074 1075 1076 1077 1078
          self.portal_selections.setSelectionParamsFor(selection_name, 
                                                       kw.copy())
          self.portal_selections.setSelectionCheckedUidsFor(
                                             selection_name, 
                                             current_uid_list)
1079 1080 1081 1082 1083 1084 1085 1086
          field_value = str(field_value).splitlines()
          REQUEST.form[field_key] = field_value
          portal_status_message = Message(
                          domain='erp5_ui',
                          message="Please select one (or more) object.")
        else:
          portal_status_message = Message(domain='erp5_ui',
                                          message="Please select one object.")
1087 1088


1089 1090 1091 1092 1093 1094
        # Save the current REQUEST form
        # We can't put FileUpload instances because we can't pickle them
        pickle_kw = {}
        for key in REQUEST.form.keys():
          if not isinstance(REQUEST.form[key],FileUpload):
            pickle_kw[key] = REQUEST.form[key]
1095
        form_pickle, form_signature = self.getPickleAndSignature(**pickle_kw)
1096

1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114
        base_category = None
        kw = {}
        kw['object_uid'] = object_uid
        kw['form_id'] = redirect_form_id
        kw['selection_name'] = selection_name
        kw['selection_index'] = 0 # We start on the first page
        kw['field_id'] = field.id
        kw['portal_type'] = [x[0] for x in field.get_value('portal_type')]
        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')
        kw['cancel_url'] = REQUEST.get('HTTP_REFERER')
        kw['previous_form_id'] = form_id
        kw[field.get_value('catalog_index')] = field_value
        kw['portal_status_message'] = portal_status_message
1115 1116 1117 1118 1119 1120
        kw['form_pickle'] = form_pickle   
        kw['form_signature'] = form_signature

         # Empty the selection (uid)
        REQUEST.form = kw # New request form
        # Define new HTTP_REFERER
Romain Courteaud's avatar
Romain Courteaud committed
1121 1122
        REQUEST.HTTP_REFERER = '%s/%s' % (o.absolute_url(),
                                          redirect_form_id)
1123 1124
        # Return the search dialog
        return getattr(o, redirect_form_id)(REQUEST=REQUEST)
1125

1126 1127
    def _aq_dynamic(self, name):
      """
1128 1129
        Generate viewSearchRelatedDocumentDialog0, 
                 viewSearchRelatedDocumentDialog1,... if necessary
1130 1131 1132
      """
      aq_base_name = getattr(aq_base(self), name, None)
      if aq_base_name == None:
1133 1134 1135
        DYNAMIC_METHOD_NAME = 'viewSearchRelatedDocumentDialog'
        method_name_length = len(DYNAMIC_METHOD_NAME)

1136
        zope_security = '__roles__'
1137
        if (name[:method_name_length] == DYNAMIC_METHOD_NAME) and \
1138
           (name[-len(zope_security):] != zope_security):
1139
          method_count_string_list = name[method_name_length:].split('_')
1140 1141 1142 1143
          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
1144
          except TypeError:
1145
            return aq_base_name
1146
          else:
1147 1148 1149
            if len(method_count_string_list) > 1:
              # be sure that method name is correct
              try:
Romain Courteaud's avatar
Romain Courteaud committed
1150
                sub_index = string.atoi(method_count_string_list[1])
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1151
              except TypeError:
1152 1153
                return aq_base_name
            else:
Romain Courteaud's avatar
Romain Courteaud committed
1154
              sub_index = None
1155

1156
            # generate dynamicaly needed forwarder methods
1157 1158
            def viewSearchRelatedDocumentDialogWrapper(self, form_id, 
                                                       REQUEST=None, **kw):
1159 1160 1161
              """
                viewSearchRelatedDocumentDialog Wrapper
              """
1162 1163
              LOG('SelectionTool.viewSearchRelatedDocumentDialogWrapper, kw', 
                  0, kw)
1164
              return self.viewSearchRelatedDocumentDialog(
1165 1166 1167 1168
                                   method_count, form_id, 
                                   REQUEST=REQUEST, sub_index=sub_index, **kw)
            setattr(self.__class__, name, 
                    viewSearchRelatedDocumentDialogWrapper)
1169 1170 1171 1172 1173 1174 1175

            klass = aq_base(self).__class__
            if hasattr(klass, 'security'):
              from Products.ERP5Type import Permissions as ERP5Permissions
              klass.security.declareProtected(ERP5Permissions.View, name)
            else:
              # XXX security declaration always failed....
1176 1177
              LOG('WARNING ERP5Form SelectionTool, security not defined on',
                  0, klass.__name__)
1178 1179 1180 1181
            return getattr(self, name)
        else:
          return aq_base_name
      return aq_base_name
1182

Jean-Paul Smets's avatar
Jean-Paul Smets committed
1183
InitializeClass( SelectionTool )
1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238

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
  def getObject(self):
    return self.object
    
  def getIsPureSummary(self):
    return self.is_pure_summary
    
  def getDepth(self):
    return self.depth
    
  def getIsOpen(self):
    return self.is_open
  
  def getSelectDomainDict(self): 
    return self.select_domain_dict
    
  def getExceptionUidList(self):
    return self.exception_uid_list
  

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'),)):
  """
    (object, is_pure_summary, depth, is_open, select_domain_dict)

    select_domain_dict is a dictionary of  associative list of (id, domain)
  """
  if type(report_path) is type('a'): report_path = report_path.split('/')

  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:
        if base_category in portal_categories.objectIds():
          if base_category == 'parent':
            # parent has a special treatment
            root = root_dict[base_category] = root_dict[None] = here
            report_path = report_path[1:]
1239
          else:
1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268
            root = root_dict[base_category] = root_dict[None] = portal_categories[base_category]
            report_path = report_path[1:]
      if root is None and portal_domains is not None:
        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 = ()
    else:
      root = root_dict[None] = root_dict[base_category]
      report_path = report_path[1:]
    is_empty_level = (root.objectCount() == 0) and (len(report_path) != 0)
    if is_empty_level: base_category = report_path[0]

  tree_list = []
  if root is None: return tree_list

  if base_category == 'parent':
    if hasattr(aq_base(root), 'objectValues'):
      # If this is a folder, try to browse the hierarchy
      for zo in root.searchFolder(sort_on=sort_on):
        o = zo.getObject()
        if o is not None:
          new_root_dict = root_dict.copy()
          new_root_dict[None] = new_root_dict[base_category] = o
1269

1270 1271 1272
          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
1273

1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284
            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())          
            tree_list += [TreeListLine(o, 1, depth, 1, selection_domain, exception_uid_list)] # Summary (open)
            if is_report_opened :
              tree_list += [TreeListLine(o, 0, depth, 0, selection_domain, exception_uid_list)] # List (contents, closed, must be strict selection)
            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)
          else:
            tree_list += [TreeListLine(o, 1, depth, 0, selection_domain, ())] # Summary (closed)
  else:
1285 1286 1287 1288 1289 1290
    # 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:
1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306
      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)
        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)
      else:

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