SelectionTool.py 38.8 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 30 31 32 33 34 35
#
# 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.
#
##############################################################################

"""\
ERP portal_categories tool.
"""

from OFS.SimpleItem import SimpleItem
from Products.CMFCore.utils import UniqueObject
from Globals import InitializeClass, DTMLFile, PersistentMapping, get_request
36
from ZTUtils import make_query
Jean-Paul Smets's avatar
Jean-Paul Smets committed
37 38 39 40
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions as ERP5Permissions
from Products.ERP5Form import _dtmldir
from Selection import Selection
Sebastien Robin's avatar
Sebastien Robin committed
41 42
from email.MIMEBase import MIMEBase
from email import Encoders
Sebastien Robin's avatar
Sebastien Robin committed
43
from copy import copy
44
from DateTime import DateTime
Sebastien Robin's avatar
Sebastien Robin committed
45
import md5
Sebastien Robin's avatar
Sebastien Robin committed
46 47 48
import pickle
import hmac
import random
Jean-Paul Smets's avatar
Jean-Paul Smets committed
49

50
import string
Jean-Paul Smets's avatar
Jean-Paul Smets committed
51
from zLOG import LOG
52 53
from Acquisition import Implicit, aq_base

Jean-Paul Smets's avatar
Jean-Paul Smets committed
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83

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'
                         }
                        ,
                        )
                     )

    security.declareProtected( ERP5Permissions.ManagePortal
                             , 'manage_overview' )
    manage_overview = DTMLFile( 'explainCategoryTool', _dtmldir )

84 85 86 87 88 89 90 91
    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
92 93 94 95 96 97 98
    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()
99

100 101 102 103 104 105 106 107
      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()
        return self.selection_data[user_id].get(selection_name, None)
      else:
Jean-Paul Smets's avatar
Jean-Paul Smets committed
108
        return None
109

Jean-Paul Smets's avatar
Jean-Paul Smets committed
110 111 112 113 114
    security.declareProtected(ERP5Permissions.View, 'setSelectionFor')
    def setSelectionFor(self, selection_name, selection_object, REQUEST=None):
      """
        Sets the selection instance for a given selection_name
      """
115 116 117
      # Set the name so that this selection itself can get its own name.
      selection_object.edit(name = selection_name)

Jean-Paul Smets's avatar
Jean-Paul Smets committed
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
      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')

155 156 157 158 159
    security.declareProtected(ERP5Permissions.View, 'getSelectionParams')
    def getSelectionParams(self, selection_name, params=None, REQUEST=None):
      """
        Returns the params in the selection
      """
160
      if params is None: params = {}
161 162
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
163 164
        if len(selection.params) > 0:
          return selection.getParams()
165 166 167 168 169
        else:
          return params
      else:
        return params

Jean-Paul Smets's avatar
Jean-Paul Smets committed
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
    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)

194 195 196 197 198 199 200 201 202 203 204
    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)

205 206 207 208 209 210 211
    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:
212
        #return selection_object.selection_checked_uids
213
        return selection_object.getCheckedUids()
214 215 216 217 218 219 220 221 222 223
      return []

    security.declareProtected(ERP5Permissions.View, 'checkAll')
    def checkAll(self, selection_name, listbox_uid, REQUEST=None):
      """
        Sets the selection params for a given selection_name
      """
      selection_object = self.getSelectionFor(selection_name, REQUEST)
      if selection_object:
        selection_uid_dict = {}
224
        for uid in selection_object.checked_uids:
225 226
          selection_uid_dict[uid] = 1
        for uid in listbox_uid:
227 228
          try:
            selection_uid_dict[int(uid)] = 1
229
          except ValueError:
230
            pass # this can happen in report
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
        self.setSelectionCheckedUidsFor(selection_name, selection_uid_dict.keys(), REQUEST=REQUEST)
      request = REQUEST
      if request:
        referer = request['HTTP_REFERER']
        referer = referer.replace('reset=', 'noreset=')
        referer = referer.replace('reset:int=', 'noreset:int=')
        return request.RESPONSE.redirect(referer)

    security.declareProtected(ERP5Permissions.View, 'uncheckAll')
    def uncheckAll(self, selection_name, listbox_uid, REQUEST=None):
      """
        Sets the selection params for a given selection_name
      """
      selection_object = self.getSelectionFor(selection_name, REQUEST)
      if selection_object:
        selection_uid_dict = {}
247
        for uid in selection_object.checked_uids:
248 249
          selection_uid_dict[uid] = 1
        for uid in listbox_uid:
250 251 252
          try:
            if selection_uid_dict.has_key(int(uid)): del selection_uid_dict[int(uid)]
          except ValueError:
253
            pass # This happens in report mode
254 255 256 257 258 259 260 261
        self.setSelectionCheckedUidsFor(selection_name, selection_uid_dict.keys(), REQUEST=REQUEST)
      request = REQUEST
      if request:
        referer = request['HTTP_REFERER']
        referer = referer.replace('reset=', 'noreset=')
        referer = referer.replace('reset:int=', 'noreset:int=')
        return request.RESPONSE.redirect(referer)

Jean-Paul Smets's avatar
Jean-Paul Smets committed
262 263 264 265 266 267 268
    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:
269
        return selection.getListUrl()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
270 271 272 273 274 275 276 277 278 279
      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:
280
        selection.edit(invert_mode=1, uids=selection_uids, checked_uids=selection_uids)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
281 282 283 284 285 286 287 288

    security.declareProtected(ERP5Permissions.View, 'setSelectionToAll')
    def setSelectionToAll(self, selection_name, REQUEST=None):
      """
        Resets the selection
      """
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
289
        selection.edit(invert_mode=0, params={}, checked_uids=[])
Jean-Paul Smets's avatar
Jean-Paul Smets committed
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328

    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')
    def setSelectionQuickSortOrder(self, selection_name, sort_on, REQUEST=None):
      """
        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)

      request = REQUEST
      referer = request['HTTP_REFERER']
      referer = referer.replace('reset=', 'noreset=')
329
      referer = referer.replace('reset:int=', 'noreset:int=')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
330 331 332 333 334 335 336 337 338
      return request.RESPONSE.redirect(referer)

    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 ()
339
      return selection.sort_on
Jean-Paul Smets's avatar
Jean-Paul Smets committed
340 341 342 343 344 345 346 347 348 349

    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')
350
    def getSelectionColumns(self, selection_name, columns=None, REQUEST=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
351 352 353
      """
        Returns the columns in the selection
      """
354
      if columns is None: columns = []
Jean-Paul Smets's avatar
Jean-Paul Smets committed
355 356
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
357 358
        if len(selection.columns) > 0:
          return selection.columns
359
      return columns
Jean-Paul Smets's avatar
Jean-Paul Smets committed
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377


    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:
378
          return selection.stats
Jean-Paul Smets's avatar
Jean-Paul Smets committed
379
        except:
380
          return stats # That is really bad programming XXX
Jean-Paul Smets's avatar
Jean-Paul Smets committed
381 382 383 384 385 386 387 388 389 390 391 392 393
      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:
394 395
        method = self.unrestrictedTraverse(selection.method_path)
        selection = selection(method = method, context=self, REQUEST=REQUEST)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
        o = selection[0]
        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:
412 413
        method = self.unrestrictedTraverse(selection.method_path)
        selection = selection(method = method, context=self, REQUEST=REQUEST)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
        o = selection[-1]
        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:
430 431
        method = self.unrestrictedTraverse(selection.method_path)
        selection = selection(method = method, context=self, REQUEST=REQUEST)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
        o = selection[(int(selection_index) + 1) % len(selection)]
        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:
448 449
        method = self.unrestrictedTraverse(selection.method_path)
        selection = selection(method = method, context=self, REQUEST=REQUEST)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
450 451 452 453 454 455 456 457 458 459
        o = selection[(int(selection_index) - 1) % len(selection)]
        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')
460
    def nextPage(self, listbox_uid, uids=None, REQUEST=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
461 462 463
      """
        Access the next page of a list
      """
464
      if uids is None: uids = []
Jean-Paul Smets's avatar
Jean-Paul Smets committed
465
      request = REQUEST
466
      #form_id = request.form_id
Jean-Paul Smets's avatar
Jean-Paul Smets committed
467 468
      selection_name = request.list_selection_name
      selection = self.getSelectionFor(selection_name, REQUEST)
469
      params = selection.getParams()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
470 471 472 473 474
      lines = params.get('list_lines',0)
      start = params.get('list_start', 0)
      params['list_start'] = int(start) + int(lines)
      selection.edit(params= params)

475 476
      self.uncheckAll(selection_name, listbox_uid)
      return self.checkAll(selection_name, uids, REQUEST=REQUEST)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
477 478

    security.declareProtected(ERP5Permissions.View, 'previousPage')
479
    def previousPage(self, listbox_uid, uids=None, REQUEST=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
480 481 482
      """
        Access the previous page of a list
      """
483
      if uids is None: uids = []
Jean-Paul Smets's avatar
Jean-Paul Smets committed
484
      request = REQUEST
485
      #form_id = request.form_id
Jean-Paul Smets's avatar
Jean-Paul Smets committed
486 487
      selection_name = request.list_selection_name
      selection = self.getSelectionFor(selection_name, REQUEST)
488
      params = selection.getParams()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
489 490 491
      lines = params.get('list_lines',0)
      start = params.get('list_start', 0)
      params['list_start'] = max(int(start) - int(lines), 0)
492
      selection.edit(params= selection.params)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
493

494 495
      self.uncheckAll(selection_name, listbox_uid)
      return self.checkAll(selection_name, uids, REQUEST=REQUEST)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
496 497

    security.declareProtected(ERP5Permissions.View, 'setPage')
498
    def setPage(self, listbox_uid, uids=None, REQUEST=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
499 500 501
      """
        Access the previous page of a list
      """
502
      if uids is None: uids = []
Jean-Paul Smets's avatar
Jean-Paul Smets committed
503
      request = REQUEST
504
      #form_id = request.form_id
Jean-Paul Smets's avatar
Jean-Paul Smets committed
505 506 507
      selection_name = request.list_selection_name
      selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
      if selection is not None:
508
        params = selection.getParams()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
509 510 511 512
        lines = params.get('list_lines',0)
        start = request.form.get('list_start',0)

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

515 516
      self.uncheckAll(selection_name, listbox_uid)
      return self.checkAll(selection_name, uids, REQUEST=REQUEST)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
517 518 519 520 521 522 523

    security.declareProtected(ERP5Permissions.View, 'setDomainRoot')
    def setDomainRoot(self, REQUEST):
      """
        Sets the root domain for the current selection
      """
      request = REQUEST
524
      #form_id = request.form_id
Jean-Paul Smets's avatar
Jean-Paul Smets committed
525 526 527
      selection_name = request.list_selection_name
      selection = self.getSelectionFor(selection_name, REQUEST)
      root_url = request.form.get('domain_root_url','portal_categories')
528
      selection.edit(domain_path=root_url, domain_list=())
Jean-Paul Smets's avatar
Jean-Paul Smets committed
529 530 531

      return request.RESPONSE.redirect(request['HTTP_REFERER'])

532 533
    security.declareProtected(ERP5Permissions.View, 'unfoldDomain')
    def unfoldDomain(self, REQUEST):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
534 535 536 537
      """
        Sets the root domain for the current selection
      """
      request = REQUEST
538
      #form_id = request.form_id
Jean-Paul Smets's avatar
Jean-Paul Smets committed
539 540
      selection_name = request.list_selection_name
      selection = self.getSelectionFor(selection_name, REQUEST)
541 542 543 544 545 546
      domain_url = request.form.get('domain_url',None)
      domain_depth = request.form.get('domain_depth',0)
      domain_list = list(selection.getDomainList())
      domain_list = domain_list[0:min(domain_depth, len(domain_list))]
      if type(domain_url) == type('a'):
        selection.edit(domain_list = domain_list + [domain_url])
Jean-Paul Smets's avatar
Jean-Paul Smets committed
547 548 549

      return request.RESPONSE.redirect(request['HTTP_REFERER'])

550 551 552 553 554 555
    security.declareProtected(ERP5Permissions.View, 'foldDomain')
    def foldDomain(self, REQUEST):
      """
        Sets the root domain for the current selection
      """
      request = REQUEST
556
      #form_id = request.form_id
557 558 559 560 561 562 563 564 565 566
      selection_name = request.list_selection_name
      selection = self.getSelectionFor(selection_name, REQUEST)
      domain_url = request.form.get('domain_url',None)
      domain_depth = request.form.get('domain_depth',0)
      domain_list = list(selection.getDomainList())
      domain_list = domain_list[0:min(domain_depth, len(domain_list))]
      selection.edit(domain_list=filter(lambda x:x != domain_url, domain_list))

      return request.RESPONSE.redirect(request['HTTP_REFERER'])

567

Jean-Paul Smets's avatar
Jean-Paul Smets committed
568 569 570 571 572 573
    security.declareProtected(ERP5Permissions.View, 'setReportRoot')
    def setReportRoot(self, REQUEST):
      """
        Sets the root domain for the current selection
      """
      request = REQUEST
574
      #form_id = request.form_id
Jean-Paul Smets's avatar
Jean-Paul Smets committed
575 576 577
      selection_name = request.list_selection_name
      selection = self.getSelectionFor(selection_name, REQUEST)
      root_url = request.form.get('report_root_url','portal_categories')
578
      selection.edit(report_path=root_url, report_list=())
Jean-Paul Smets's avatar
Jean-Paul Smets committed
579 580 581 582 583 584 585 586

      return request.RESPONSE.redirect(request['HTTP_REFERER'])


    security.declareProtected(ERP5Permissions.View, 'unfoldReport')
    def unfoldReport(self, REQUEST):
      """
        Sets the root domain for the current selection
587

588
        report_list is a list of relative_url of category, domain, etc.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
589 590
      """
      request = REQUEST
591
      #form_id = request.form_id
Jean-Paul Smets's avatar
Jean-Paul Smets committed
592 593
      selection_name = request.list_selection_name
      selection = self.getSelectionFor(selection_name, REQUEST)
594
      report_url = request.form.get('report_url',None)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
595
      if type(report_url) == type('a'):
596
        selection.edit(report_list=list(selection.getReportList()) + [report_url])
Jean-Paul Smets's avatar
Jean-Paul Smets committed
597

598
      referer = request['HTTP_REFERER']
599
      referer = referer.replace('report_depth:int=', 'noreport_depth:int=')
600
      return request.RESPONSE.redirect(referer)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
601 602 603 604 605 606 607

    security.declareProtected(ERP5Permissions.View, 'foldReport')
    def foldReport(self, REQUEST):
      """
        Sets the root domain for the current selection
      """
      request = REQUEST
608
      #form_id = request.form_id
Jean-Paul Smets's avatar
Jean-Paul Smets committed
609 610
      selection_name = request.list_selection_name
      selection = self.getSelectionFor(selection_name, REQUEST)
611
      report_url = request.form.get('report_url',None)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
612
      if type(report_url) == type('a'):
613 614
        report_list = selection.getReportList()
        selection.edit(report_list=filter(lambda x:x != report_url, report_list))
Jean-Paul Smets's avatar
Jean-Paul Smets committed
615

616
      referer = request['HTTP_REFERER']
617
      referer = referer.replace('report_depth:int=', 'noreport_depth:int=')
618
      return request.RESPONSE.redirect(referer)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646


    security.declareProtected(ERP5Permissions.View, 'setListboxDisplayMode')
    def setListboxDisplayMode(self, REQUEST,listbox_display_mode):
      """
        Toogle display of the listbox
      """

      request = REQUEST
      selection_name = request.list_selection_name
      selection = self.getSelectionFor(selection_name, REQUEST)

      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

      selection.edit(flat_list_mode=flat_list_mode,domain_tree_mode=domain_tree_mode,
                                                report_tree_mode=report_tree_mode)

647
      # It is better to reset the query when changing the display mode.
648 649
      params = selection.getParams()
      if 'where_expression' in params: del params['where_expression']
650 651
      selection.edit(params = params)

Jean-Paul Smets's avatar
Jean-Paul Smets committed
652 653
      referer = request['HTTP_REFERER']
      referer = referer.replace('reset=', 'noreset=')
654
      referer = referer.replace('reset:int=', 'noreset:int=')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683
      return request.RESPONSE.redirect(referer)


    security.declareProtected(ERP5Permissions.View, 'setFlatListMode')
    def setFlatListMode(self, REQUEST):
      """
        Set display of the listbox to FlatList mode
      """

      return self.setListboxDisplayMode(REQUEST=REQUEST,listbox_display_mode='FlatListMode')


    security.declareProtected(ERP5Permissions.View, 'setDomainTreeMode')
    def setDomainTreeMode(self, REQUEST):
      """
         Set display of the listbox to DomainTree mode
      """

      return self.setListboxDisplayMode(REQUEST=REQUEST,listbox_display_mode='DomainTreeMode')


    security.declareProtected(ERP5Permissions.View, 'setReportTreeMode')
    def setReportTreeMode(self, REQUEST):
      """
        Set display of the listbox to ReportTree mode
      """

      return self.setListboxDisplayMode(REQUEST=REQUEST,listbox_display_mode='ReportTreeMode')

684 685 686 687 688 689
    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)
690 691
      if selection is None:
        return []
692
      return selection(method=selection_method, context=context, REQUEST=REQUEST)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
693

694 695 696 697 698 699
    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)
700 701
      if selection is None:
        return []
702
      uid_list = selection.getCheckedUids()
703 704 705 706 707 708 709 710 711 712
      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:
713
        value_list = self.getSelectionSelectedValueList(selection_name, REQUEST=REQUEST, method=selection_method, context=context)
714
      return value_list
Jean-Paul Smets's avatar
Jean-Paul Smets committed
715

Jean-Paul Smets's avatar
Jean-Paul Smets committed
716 717 718 719 720 721 722
    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
723 724 725 726 727
    security.declareProtected(ERP5Permissions.View, 'selectionHasChanged')
    def selectionHasChanged(self, md5_string, object_uid_list):
      """
        We want to be sure that the selection did not change
      """
Yoshinori Okuji's avatar
Yoshinori Okuji committed
728 729
      #LOG('selectionHasChanged, md5_string',0,md5_string)
      #LOG('selectionHasChanged, object_uid_list',0,object_uid_list)
Sebastien Robin's avatar
Sebastien Robin committed
730 731
      sorted_object_uid_list = copy(object_uid_list)
      sorted_object_uid_list.sort()
Sebastien Robin's avatar
Sebastien Robin committed
732
      new_md5_string = md5.new(str(sorted_object_uid_list)).hexdigest()
Yoshinori Okuji's avatar
Yoshinori Okuji committed
733
      #LOG('selectionHasChanged, new_md5_string',0,new_md5_string)
Sebastien Robin's avatar
Sebastien Robin committed
734
      if md5_string != new_md5_string:
Yoshinori Okuji's avatar
Yoshinori Okuji committed
735
        #LOG('selectionHasChanged, return...',0,'True')
Sebastien Robin's avatar
Sebastien Robin committed
736
        return True
Yoshinori Okuji's avatar
Yoshinori Okuji committed
737
      #LOG('selectionHasChanged, return...',0,'False')
Sebastien Robin's avatar
Sebastien Robin committed
738 739
      return False

740 741
    security.declareProtected(ERP5Permissions.View, 'getPickle')
    def getPickle(self,**kw):
Sebastien Robin's avatar
Sebastien Robin committed
742 743 744 745
      """
      we give many keywords and we will get the corresponding
      pickle string and signature
      """
Yoshinori Okuji's avatar
Yoshinori Okuji committed
746
      #LOG('getPickle kw',0,kw)
747 748 749
      # 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
750
        if isinstance(v,DateTime):
751 752
          del kw[k]
      # XXX End of the part to remove
Sebastien Robin's avatar
Sebastien Robin committed
753 754 755 756 757 758
      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','@@@')
759 760 761 762 763 764 765 766
      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
      """
767
      cookie_password = self._getCookiePassword()
768
      pickle_string = self.getPickle(**kw)
Sebastien Robin's avatar
Sebastien Robin committed
769 770 771
      signature = hmac.new(cookie_password,pickle_string).hexdigest()
      return (pickle_string,signature)

772 773 774 775 776 777 778 779 780 781 782 783 784 785
    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
786 787 788 789 790 791 792 793 794
    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:
795
        object = self.getObjectFromPickle(pickle_string)
Sebastien Robin's avatar
Sebastien Robin committed
796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835
      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

836
    # Related document searching
837
    def viewSearchRelatedDocumentDialog(self, index, form_id, REQUEST=None, method_count2=None, **kw):
838 839 840 841 842
      """
        Returns a search related document dialog
        
        A set of forwarders us defined to circumvent limitations of HTML
      """
843 844 845
      if method_count2 != None:
        REQUEST.form['method_count2'] = method_count2
      
846 847 848 849
      # Save the current REQUEST form
      form_pickle, form_signature = self.getPickleAndSignature(**REQUEST.form)
      REQUEST.form_pickle = form_pickle
      REQUEST.form_signature = form_signature
850

851 852 853 854
      # Find the field which was clicked on
      form = getattr(self, form_id)
      field = None
      relation_index = 0
Romain Courteaud's avatar
Romain Courteaud committed
855

856
      for field in form.get_fields(include_disabled=0):
Romain Courteaud's avatar
Romain Courteaud committed
857 858 859 860 861
        if getattr(field, 'is_relation_field', None):
          if index == relation_index:
            break
          else:
            relation_index += 1
862
      
Romain Courteaud's avatar
Romain Courteaud committed
863
      #return "Done %s %s %s %s %s" % (index, relation_index, form_id, field, form_pickle)
864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895

      # Find the object which needs to be updated      
      object_uid = REQUEST.get('object_uid', None)
      object_path = REQUEST.get('object_uid', None)
      if object_uid is not None:
        o = self.portal_catalog.getObject(object_uid)
      else:
        o = None

      if o is None:
        # we first try to reindex the object, thanks to the object_path
        if object_path is not None:
          o = context.restrictedTraverse(object_path)
        if o is not None:
          o.immediateReindexObject()
          object_uid = o.getUid() 
        else:
          return "Sorrry, Error, the calling object was not catalogued. Do not know how to do ?"
        
      base_category = None
      kw = {}
      
      kw['object_uid'] = object_uid
      kw['form_id'] = 'Base_viewRelatedObjectList'
      kw['selection_name'] = 'Base_viewRelatedObjectList'
      kw['selection_index'] = 0 # We start on the first page
      kw['field_id'] = field.id
      kw['portal_type'] = map(lambda x:x[0],field.get_value('portal_type'))
      kw['reset'] = 1
      kw['base_category'] = field.get_value( 'base_category')
      kw['cancel_url'] = REQUEST.get('HTTP_REFERER')
      kw['previous_form_id'] = form_id        
896 897


898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916
      kw[field.get_value('catalog_index')] = REQUEST.form['field_%s' % field.id]
      
      """
      # We work with strings - ie. single values
      kw ={}
      context.portal_selections.setSelectionParamsFor('Base_viewRelatedObjectList', kw.copy())
      previous_uids = o.getValueUids(base_category, portal_type=portal_type)
      relation_list = context.portal_catalog(**kw)
      relation_uid_list = map(lambda x: x.uid, relation_list)
      uids = []
      """
      
      # Empty the selection (uid)
      REQUEST.form = kw # New request form
                  
      # Define new HTTP_REFERER
      REQUEST.HTTP_REFERER = '%s/Base_viewRelatedObjectList' % o.absolute_url() 
      
      # Return the search dialog
917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951
      return o.Base_viewRelatedObjectList(REQUEST=REQUEST)


     # XXX do not use this method, use aq_dynamic (JPS)
#    def __getattr__(self, name):
#      dynamic_method_name = 'viewSearchRelatedDocumentDialog'
#      if name[:len(dynamic_method_name)] == dynamic_method_name:
#        method_count_string = name[len(dynamic_method_name):]
#        # be sure that method name is correct
#        try:
#          import string
#          method_count = string.atoi(method_count_string)
#        except:
#          raise AttributeError, name
#        else:
#          # generate dynamicaly needed forwarder methods
#          def viewSearchRelatedDocumentDialogWrapper(self, form_id, REQUEST=None, **kw):
#            """
#              viewSearchRelatedDocumentDialog Wrapper
#            """
#            return self.viewSearchRelatedDocumentDialog(method_count, form_id, REQUEST=REQUEST, **kw)
#          
#          setattr(self.__class__, name, viewSearchRelatedDocumentDialogWrapper)
#
#          klass = 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....
#            LOG('WARNING ERP5Form SelectionTool, security not defined on',0,klass.__name__)
#
#          return getattr(self, name)
#      else:
#        raise AttributeError, name
952

953 954 955 956 957 958
    def _aq_dynamic(self, name):
      """
        Generate viewSearchRelatedDocumentDialog0, viewSearchRelatedDocumentDialog1,... if necessary
      """
      aq_base_name = getattr(aq_base(self), name, None)
      if aq_base_name == None:
959

960 961 962 963 964 965 966
        dynamic_method_name = 'viewSearchRelatedDocumentDialog'
        zope_security = '__roles__'
        if (name[:len(dynamic_method_name)] == dynamic_method_name) and (name[-len(zope_security):] != zope_security) :

          #method_count_string = name[len(dynamic_method_name):]

          method_count_string_list = string.split( name[len(dynamic_method_name):] , '_' )
967

968 969 970 971 972 973 974 975
          method_count_string = method_count_string_list[0]

          
          # be sure that method name is correct
          try:
            method_count = string.atoi(method_count_string)
          except:
            return aq_base_name
976 977
          else:

978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
            if len(method_count_string_list) > 1:
              # be sure that method name is correct
              try:
                method_count2 = string.atoi(method_count_string_list[1])
              except:
                return aq_base_name
            else:
              method_count2 = None


            
            # generate dynamicaly needed forwarder methods
            def viewSearchRelatedDocumentDialogWrapper(self, form_id, REQUEST=None, **kw):
              """
                viewSearchRelatedDocumentDialog Wrapper
              """
              if method_count2 == None:
                return self.viewSearchRelatedDocumentDialog(method_count, form_id, REQUEST=REQUEST, **kw)
              else:
                return self.viewSearchRelatedDocumentDialog(method_count, form_id, REQUEST=REQUEST, method_count2=method_count2, **kw)
            
            setattr(self.__class__, name, viewSearchRelatedDocumentDialogWrapper)

            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....
              LOG('WARNING ERP5Form SelectionTool, security not defined on',0,klass.__name__)

            return getattr(self, name)
        else:
          return aq_base_name
      return aq_base_name
1013

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