ERP5Site.py 51.5 KB
Newer Older
1
##############################################################################
Jean-Paul Smets's avatar
Jean-Paul Smets committed
2 3 4 5 6 7 8 9 10 11 12
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
13 14
"""
  Portal class
Jean-Paul Smets's avatar
Jean-Paul Smets committed
15 16 17
"""

import Globals
18
import AccessControl
Jean-Paul Smets's avatar
Jean-Paul Smets committed
19
from Globals import package_home
20

21
from ZPublisher import BeforeTraverse
Jean-Paul Smets's avatar
Jean-Paul Smets committed
22 23 24 25
from AccessControl import ClassSecurityInfo
from Products.CMFDefault.Portal import CMFSite, PortalGenerator
from Products.CMFCore.utils import getToolByName, _getAuthenticatedUser
from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
Jean-Paul Smets's avatar
Jean-Paul Smets committed
26
from Products.ERP5Type.Document.Folder import FolderMixIn
27
from Products.ERP5Type.Document import addFolder
Jean-Paul Smets's avatar
Jean-Paul Smets committed
28
from Acquisition import aq_base, aq_parent, aq_inner, aq_acquire
29
from Products.ERP5Type import allowClassTool
30
from Products.ERP5Type.Cache import CachingMethod
31 32
from Products.ERP5Type.ERP5Type import ERP5TypeInformation
from Products.ERP5.Document.BusinessTemplate import BusinessTemplate
33

34
import ERP5Defaults
Jean-Paul Smets's avatar
Jean-Paul Smets committed
35

36
from zLOG import LOG, INFO
Sebastien Robin's avatar
Sebastien Robin committed
37
from string import join
38
import os, traceback
39 40
MARKER = []

41

Jean-Paul Smets's avatar
Jean-Paul Smets committed
42
# Site Creation DTML
43 44
manage_addERP5SiteForm = Globals.HTMLFile('dtml/addERP5Site', globals())
manage_addERP5SiteForm.__name__ = 'addERP5Site'
Jean-Paul Smets's avatar
Jean-Paul Smets committed
45 46

# ERP5Site Constructor
47 48 49
def manage_addERP5Site(self,
                       id,
                       title='ERP5',
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
                       description='',
                       create_userfolder=1,
                       create_activities=1,
                       email_from_address='postmaster@localhost',
                       email_from_name='Portal Administrator',
                       validate_email=0,
                       erp5_sql_connection_type='Z MySQL Database Connection',
                       erp5_sql_connection_string='test test',
                       erp5_sql_deferred_connection_type = \
                           'Z MySQL Deferred Database Connection',
                       erp5_sql_deferred_connection_string = 'test test',
                       cmf_activity_sql_connection_type= \
                           'Z MySQL Database Connection',
                       cmf_activity_sql_connection_string='test test',
                       light_install=0,
                       reindex=1,
                       RESPONSE=None):
  '''
  Adds a portal instance.
  '''
70
  gen = ERP5Generator()
71
  id = str(id).strip()
72 73
  p = gen.create(self,
                 id,
74 75 76 77 78 79 80 81 82 83
                 create_userfolder,
                 erp5_sql_connection_type,
                 erp5_sql_connection_string,
                 erp5_sql_deferred_connection_type,
                 erp5_sql_deferred_connection_string,
                 cmf_activity_sql_connection_type,
                 cmf_activity_sql_connection_string,
                 create_activities=create_activities,
                 light_install=light_install,
                 reindex=reindex)
84 85
  gen.setupDefaultProperties(p,
                             title,
86
                             description,
87
                             email_from_address,
88 89
                             email_from_name,
                             validate_email)
90
  if RESPONSE is not None:
91 92
    RESPONSE.redirect(p.absolute_url())

93 94 95 96 97 98 99 100 101 102 103 104 105 106 107

class ReferCheckerBeforeTraverseHook:
  """This before traverse hook checks the HTTP_REFERER argument in the request
  and refuses access to anything else that portal_url.

  This is enabled by calling the method enableRefererCheck on the portal.
  """
  handle = '_erp5_referer_check'

  def __call__(self, container, request):
    """Checks the request contains a valid referrer.
    """
    response = request.RESPONSE
    http_url = request.get('ACTUAL_URL', '').strip()
    http_referer = request.get('HTTP_REFERER', '').strip()
108

109 110 111 112 113 114 115
    user_password = request._authUserPW()
    if user_password:
      user = container.acl_users.getUserById(user_password[0]) or\
              container.aq_parent.acl_users.getUserById(user_password[0])
      # Manager can do anything
      if user is not None and 'Manager' in user.getRoles():
        return
116

117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
    portal_url = container.portal_url.getPortalObject().absolute_url()
    if http_referer != '':
      # if HTTP_REFERER is set, user can acces the object if referer is ok
      if http_referer.startswith(portal_url):
        return
      LOG('HTTP_REFERER_CHECK : BAD REFERER !', INFO,
          'request : "%s", referer : "%s"' % (http_url, http_referer))
      response.unauthorized()
    else:
      # no HTTP_REFERER, we only allow to reach portal_url
      for i in ('/', '/index_html', '/login_form', '/view'):
        if http_url.endswith(i):
          http_url = http_url[:-len(i)]
          break
      if len(http_url) == 0 or not portal_url.startswith(http_url):
        LOG('HTTP_REFERER_CHECK : NO REFERER !', INFO,
            'request : "%s"' % http_url)
        response.unauthorized()

136
class ERP5Site(FolderMixIn, CMFSite):
137
  """
138 139
  The *only* function this class should have is to help in the setup
  of a new ERP5.  It should not assist in the functionality at all.
140
  """
141
  meta_type = 'ERP5 Site'
142
  constructors = (manage_addERP5SiteForm, manage_addERP5Site, )
143 144 145
  uid = 0
  last_id = 0
  icon = 'portal.gif'
146
  isIndexable = 1 # Default value, prevents error during upgrade
147 148

  _properties = (
149
      { 'id':'title',
150
        'type':'string'},
151
      { 'id':'description',
152 153
        'type':'text'},
      )
154 155 156 157 158 159 160 161 162
  title = ''
  description = ''

  # Declarative security
  security = ClassSecurityInfo()
  security.declareObjectProtected(Permissions.AccessContentsInformation)

  security.declareProtected(Permissions.View, 'view')
  def view(self):
163 164 165 166 167
    """
      Returns the default view.
      Implemented for consistency
    """
    return self.index_html()
168

169 170 171 172 173 174 175
  security.declareProtected(Permissions.ManagePortal, 'enableRefererCheck')
  def enableRefererCheck(self):
    """Enable a ReferCheckerBeforeTraverseHook to check users have valid
    HTTP_REFERER
    """
    BeforeTraverse.registerBeforeTraverse(self,
                                        ReferCheckerBeforeTraverseHook(),
176 177 178
                                        ReferCheckerBeforeTraverseHook.handle,
                             # we want to be registered _after_ CookieCrumbler
                                        100)
179

180 181 182 183
  def _disableRefererCheck(self):
    """Disable the HTTP_REFERER check."""
    BeforeTraverse.unregisterBeforeTraverse(self,
                                        ReferCheckerBeforeTraverseHook.handle)
184

185
  def hasObject(self, id):
186
    """
187
    Check if the portal has an id.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
188
    """
189
    return id in self.objectIds()
190

191
  security.declareProtected(Permissions.AccessContentsInformation,
192
                            'getPortalObject')
193 194
  def getPortalObject(self):
    return self
Jean-Paul Smets's avatar
Jean-Paul Smets committed
195

196
  security.declareProtected(Permissions.AccessContentsInformation,
197
                            'getTitle')
198 199 200 201 202
  def getTitle(self):
    """
      Return the title.
    """
    return self.title
203

204 205 206 207 208 209
  security.declareProtected(Permissions.AccessContentsInformation, 'getUid')
  def getUid(self):
    """
      Returns the UID of the object. Eventually reindexes
      the object in order to make sure there is a UID
      (useful for import / export).
Jean-Paul Smets's avatar
Jean-Paul Smets committed
210

211 212 213
      WARNING : must be updates for circular references issues
    """
    return getattr(self, 'uid', 0)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
214

215
  security.declareProtected(Permissions.AccessContentsInformation,
216
                            'getParentUid')
217 218 219 220 221
  def getParentUid(self):
    """
      A portal has no parent
    """
    return self.getUid()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
222

223 224 225 226
  # Required to allow content creation outside folders
  security.declareProtected(Permissions.View, 'getIdGroup')
  def getIdGroup(self):
    return None
Jean-Paul Smets's avatar
Jean-Paul Smets committed
227

228 229 230 231
  # Required to allow content creation outside folders
  security.declareProtected(Permissions.ModifyPortalContent, 'setLastId')
  def setLastId(self, id):
    self.last_id = id
Jean-Paul Smets's avatar
Jean-Paul Smets committed
232

233 234
  security.declareProtected(Permissions.AccessContentsInformation, 'getUrl')
  def getUrl(self, REQUEST=None):
235 236 237 238
    """
      Returns the absolute path of an object
    """
    return join(self.getPhysicalPath(),'/')
Sebastien Robin's avatar
Sebastien Robin committed
239

240 241 242 243 244
  # Old name - for compatibility
  security.declareProtected(Permissions.AccessContentsInformation, 'getPath')
  getPath = getUrl

  security.declareProtected(Permissions.AccessContentsInformation, 'searchFolder')
245 246 247 248 249 250 251 252
  def searchFolder(self, **kw):
    """
      Search the content of a folder by calling
      the portal_catalog.
    """
    if not kw.has_key('parent_uid'):
      kw['parent_uid'] = self.uid
    kw2 = {}
253 254 255
    # Remove useless matter before calling the
    # catalog. In particular, consider empty
    # strings as None values
256
    for cname in kw.keys():
257
      if kw[cname] not in ('', None):
258
        kw2[cname] = kw[cname]
259 260
    # The method to call to search the folder
    # content has to be called z_search_folder
261 262 263 264 265 266 267 268 269 270 271 272
    method = self.portal_catalog.searchResults
    return method(**kw2)

  security.declareProtected(Permissions.AccessContentsInformation, 'countFolder')
  def countFolder(self, **kw):
    """
      Count the content of a folder by calling
      the portal_catalog.
    """
    if not kw.has_key('parent_uid'):
      kw['parent_uid'] = self.uid
    kw2 = {}
273 274 275
    # Remove useless matter before calling the
    # catalog. In particular, consider empty
    # strings as None values
276
    for cname in kw.keys():
277
      if kw[cname] not in ('', None):
278
        kw2[cname] = kw[cname]
279 280
    # The method to call to search the folder
    # content has to be called z_search_folder
281 282 283 284 285 286 287 288 289 290
    method = self.portal_catalog.countResults
    return method(**kw2)

  # Proxy methods for security reasons
  def getOwnerInfo(self):
    return self.owner_info()

  security.declarePublic('getOrderedGlobalActionList')
  def getOrderedGlobalActionList(self, action_list):
    """
291 292
    Returns a dictionnary of actions, sorted by type of object

293
    This should absolutely be rewritten by using clean
294
    concepts to separate worklists XXX
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
    """
    sorted_workflow_actions = {}
    sorted_global_actions = []
    other_global_actions = []
    for action in action_list:
      action['disabled'] = 0
      if action.has_key('workflow_title'):
        if not sorted_workflow_actions.has_key(action['workflow_title']):
          sorted_workflow_actions[action['workflow_title']] = []
        sorted_workflow_actions[action['workflow_title']].append(action)
      else:
        other_global_actions.append(action)
    workflow_title_list = sorted_workflow_actions.keys()
    workflow_title_list.sort()
    for key in workflow_title_list:
      sorted_global_actions.append({'title': key, 'disabled': 1})
      sorted_global_actions.extend(sorted_workflow_actions[key])
    sorted_global_actions.append({'title': 'Others', 'disabled': 1})
    sorted_global_actions.extend(other_global_actions)
    return sorted_global_actions

  def setupDefaultProperties(self, p, title, description,
317 318 319
                             email_from_address, email_from_name,
                             validate_email
                             ):
320
    CMFSite.setupDefaultProperties(self, p, title, description,
321 322
                           email_from_address, email_from_name,
                           validate_email)
323

324 325 326
  # Portal methods are based on the concept of having portal-specific
  # parameters for customization. In the past, we used global parameters,
  # but it was not very good because it was very difficult
327
  # to customize the settings for each portal site.
328 329
  def _getPortalConfiguration(self, id):
    """
330
    Get a portal-specific configuration.
331

332 333
    Current implementation is using properties in a portal object.
    If not found, try to get a default value for backward compatibility.
334

335 336
    This implementation can be improved by gathering information
    from appropriate places, such as portal_types, portal_categories
337
    and portal_workflow.
338 339 340
    """
    if self.hasProperty(id):
      return self.getProperty(id)
341

342 343
    # Fall back to the default.
    return getattr(ERP5Defaults, id, None)
344

345
  def _getPortalGroupedTypeList(self, group):
346
    """
347
    Return a list of portal types classified to a specific group.
348 349 350 351 352 353 354 355
    """
    def getTypeList(group):
      type_list = []
      for pt in self.portal_types.objectValues():
        if group in getattr(pt, 'group_list', ()):
          type_list.append(pt.getId())
      return tuple(type_list)

356
    getTypeList = CachingMethod(getTypeList,
357
                                id=('_getPortalGroupedTypeList', group),
358
                                cache_duration=3600)
359 360 361
    return getTypeList(group)

  def _getPortalGroupedCategoryList(self, group):
362
    """
363
    Return a list of base categories classified to a specific group.
364 365 366 367 368 369 370 371
    """
    def getCategoryList(group):
      category_list = []
      for bc in self.portal_categories.objectValues():
        if group in bc.getCategoryTypeList():
          category_list.append(bc.getId())
      return tuple(category_list)

372 373
    getCategoryList = CachingMethod(
                            getCategoryList,
374
                            id=('_getPortalGroupedCategoryList', group),
375
                            cache_duration=3600)
376 377 378
    return getCategoryList(group)

  def _getPortalGroupedStateList(self, group):
379
    """
380
    Return a list of workflow states classified to a specific group.
381 382 383 384 385 386 387 388 389 390
    """
    def getStateList(group):
      state_dict = {}
      for wf in self.portal_workflow.objectValues():
        if getattr(wf, 'states', None):
          for state in wf.states.objectValues():
            if group in getattr(state, 'type_list', ()):
              state_dict[state.getId()] = None
      return tuple(state_dict.keys())

391
    getStateList = CachingMethod(getStateList,
392
                                 id=('_getPortalGroupedStateList', group),
393
                                 cache_duration=3600)
394 395
    return getStateList(group)

396 397
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalDefaultSectionCategory')
398 399
  def getPortalDefaultSectionCategory(self):
    """
400
    Return a default section category. This method is deprecated.
401 402 403 404
    """
    LOG('ERP5Site', 0, 'getPortalDefaultSectionCategory is deprecated;'+
        ' use portal_preferences.getPreferredSectionCategory instead.')
    section_category = self.portal_preferences.getPreferredSectionCategory()
405

406 407 408 409
    # XXX This is only for backward-compatibility.
    if not section_category:
      section_category = self._getPortalConfiguration(
                                'portal_default_section_category')
410

411
    return section_category
412

413 414 415 416 417 418
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalResourceTypeList')
  def getPortalResourceTypeList(self):
    """
      Return resource types.
    """
419 420 421
    return self._getPortalGroupedTypeList('resource') or \
           self._getPortalConfiguration('portal_resource_type_list')

422 423 424 425 426 427 428
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalSubVariationTypeList')
  def getPortalSubVariationTypeList(self):
    """
      Return resource types.
    """
    return self._getPortalGroupedTypeList('sub_variation')
429

430 431 432 433 434 435 436
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalSubVariationBaseCategoryList')
  def getPortalSubVariationBaseCategoryList(self):
    """
      Return variation base categories.
    """
    return self._getPortalGroupedCategoryList('sub_variation')
437

438 439 440 441 442 443
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalVariationTypeList')
  def getPortalVariationTypeList(self):
    """
      Return variation types.
    """
444 445 446
    return self._getPortalGroupedTypeList('variation') or \
           self._getPortalConfiguration('portal_variation_type_list')

447 448 449 450 451 452
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalNodeTypeList')
  def getPortalNodeTypeList(self):
    """
      Return node types.
    """
453 454 455
    return self._getPortalGroupedTypeList('node') or \
           self._getPortalConfiguration('portal_node_type_list')

456 457 458 459 460 461
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalPaymentNodeTypeList')
  def getPortalPaymentNodeTypeList(self):
    """
      Return payment node types.
    """
462 463 464
    return self._getPortalGroupedTypeList('payment_node') or \
           self._getPortalConfiguration('portal_payment_node_type_list')

465 466 467 468 469 470
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalInvoiceTypeList')
  def getPortalInvoiceTypeList(self):
    """
      Return invoice types.
    """
471 472 473
    return self._getPortalGroupedTypeList('invoice') or \
           self._getPortalConfiguration('portal_invoice_type_list')

474 475 476 477 478 479
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalOrderTypeList')
  def getPortalOrderTypeList(self):
    """
      Return order types.
    """
480 481 482
    return self._getPortalGroupedTypeList('order') or \
           self._getPortalConfiguration('portal_order_type_list')

483 484 485 486 487 488
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalDeliveryTypeList')
  def getPortalDeliveryTypeList(self):
    """
      Return delivery types.
    """
489 490 491
    return self._getPortalGroupedTypeList('delivery') or \
           self._getPortalConfiguration('portal_delivery_type_list')

492 493 494 495 496 497
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalTransformationTypeList')
  def getPortalTransformationTypeList(self):
    """
      Return transformation types.
    """
498 499 500
    return self._getPortalGroupedTypeList('transformation') or \
           self._getPortalConfiguration('portal_transformation_type_list')

501 502 503 504 505 506
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalVariationBaseCategoryList')
  def getPortalVariationBaseCategoryList(self):
    """
      Return variation base categories.
    """
507 508 509
    return self._getPortalGroupedCategoryList('variation') or \
           self._getPortalConfiguration('portal_variation_base_category_list')

510 511 512 513 514 515
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalOptionBaseCategoryList')
  def getPortalOptionBaseCategoryList(self):
    """
      Return option base categories.
    """
516 517 518
    return self._getPortalGroupedCategoryList('option') or \
           self._getPortalConfiguration('portal_option_base_category_list')

519 520 521 522 523 524
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalInvoiceMovementTypeList')
  def getPortalInvoiceMovementTypeList(self):
    """
      Return invoice movement types.
    """
525 526 527
    return self._getPortalGroupedTypeList('invoice_movement') or \
           self._getPortalConfiguration('portal_invoice_movement_type_list')

528 529 530 531 532 533
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalOrderMovementTypeList')
  def getPortalOrderMovementTypeList(self):
    """
      Return order movement types.
    """
534 535 536
    return self._getPortalGroupedTypeList('order_movement') or \
           self._getPortalConfiguration('portal_order_movement_type_list')

537 538 539 540 541 542
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalDeliveryMovementTypeList')
  def getPortalDeliveryMovementTypeList(self):
    """
      Return delivery movement types.
    """
543 544 545
    return self._getPortalGroupedTypeList('delivery_movement') or \
           self._getPortalConfiguration('portal_delivery_movement_type_list')

546
  security.declareProtected(Permissions.AccessContentsInformation,
547
                            'getPortalSupplyTypeList')
548 549 550 551
  def getPortalSupplyTypeList(self):
    """
      Return supply types.
    """
552 553 554
    return self._getPortalGroupedTypeList('supply') or \
           self._getPortalConfiguration('portal_supply_type_list')

555
  security.declareProtected(Permissions.AccessContentsInformation,
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
                              'getPortalDocumentTypeList')
  def getPortalDocumentTypeList(self):
    """
      Return document types.
    """
    return self._getPortalGroupedTypeList('dms_document')

  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalWebDocumentTypeList')
  def getPortalWebDocumentTypeList(self):
    """
      Return web page types.
    """
    return self._getPortalGroupedTypeList('web_document')

571
  security.declareProtected(Permissions.AccessContentsInformation,
572
                            'getPortalSupplyPathTypeList')
573 574 575 576
  def getPortalSupplyPathTypeList(self):
    """
      Return supply movement types.
    """
577 578 579
    return self._getPortalGroupedTypeList('supply_path') or \
           self._getPortalConfiguration('portal_supply_path_type_list')

580 581 582 583 584 585 586
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalAcquisitionMovementTypeList')
  def getPortalAcquisitionMovementTypeList(self):
    """
      Return acquisition movement types.
    """
    return tuple(list(self.getPortalOrderMovementTypeList()) +
587 588 589
                 list(self.getPortalDeliveryMovementTypeList()) +
                 list(self.getPortalInvoiceMovementTypeList()))

590 591 592 593 594 595 596
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalMovementTypeList')
  def getPortalMovementTypeList(self):
    """
      Return movement types.
    """
    return tuple(list(self.getPortalOrderMovementTypeList()) +
597 598
                 list(self.getPortalDeliveryMovementTypeList()) +
                 list(self.getPortalInvoiceMovementTypeList()) +
599
                 ['Simulation Movement'])
600

601 602 603 604 605 606
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalSimulatedMovementTypeList')
  def getPortalSimulatedMovementTypeList(self):
    """
      Return simulated movement types.
    """
607
    return tuple([x for x in self.getPortalMovementTypeList() \
608
                  if x not in self.getPortalContainerTypeList()])
609

610 611 612 613 614 615
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalContainerTypeList')
  def getPortalContainerTypeList(self):
    """
      Return container types.
    """
616 617 618
    return self._getPortalGroupedTypeList('container') or \
           self._getPortalConfiguration('portal_container_type_list')

619 620 621 622 623 624
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalContainerLineTypeList')
  def getPortalContainerLineTypeList(self):
    """
      Return container line types.
    """
625 626 627
    return self._getPortalGroupedTypeList('container_line') or \
           self._getPortalConfiguration('portal_container_line_type_list')

628 629 630 631 632 633
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalItemTypeList')
  def getPortalItemTypeList(self):
    """
      Return item types.
    """
634 635 636
    return self._getPortalGroupedTypeList('item') or \
           self._getPortalConfiguration('portal_item_type_list')

637 638 639 640 641 642
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalDiscountTypeList')
  def getPortalDiscountTypeList(self):
    """
      Return discount types.
    """
643 644 645
    return self._getPortalGroupedTypeList('discount') or \
           self._getPortalConfiguration('portal_discount_type_list')

646 647 648 649 650 651
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalAlarmTypeList')
  def getPortalAlarmTypeList(self):
    """
      Return alarm types.
    """
652 653 654
    return self._getPortalGroupedTypeList('alarm') or \
           self._getPortalConfiguration('portal_alarm_type_list')

655 656 657 658 659 660
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalPaymentConditionTypeList')
  def getPortalPaymentConditionTypeList(self):
    """
      Return payment condition types.
    """
661 662 663
    return self._getPortalGroupedTypeList('payment_condition') or \
           self._getPortalConfiguration('portal_payment_condition_type_list')

664 665 666 667 668 669
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalBalanceTransactionLineTypeList')
  def getPortalBalanceTransactionLineTypeList(self):
    """
      Return balance transaction line types.
    """
670
    return self._getPortalGroupedTypeList('balance_transaction_line') or \
671 672
           self._getPortalConfiguration(
                  'portal_balance_transaction_line_type_list')
673

674 675 676 677 678 679
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalCurrentInventoryStateList')
  def getPortalCurrentInventoryStateList(self):
    """
      Return current inventory states.
    """
680 681 682
    return self._getPortalGroupedStateList('current_inventory') or \
           self._getPortalConfiguration('portal_current_inventory_state_list')

683 684 685 686 687 688
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalDraftOrderStateList')
  def getPortalDraftOrderStateList(self):
    """
      Return draft order states.
    """
689 690 691
    return self._getPortalGroupedStateList('draft_order') or \
           self._getPortalConfiguration('portal_draft_order_state_list')

692 693 694 695 696 697
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalPlannedOrderStateList')
  def getPortalPlannedOrderStateList(self):
    """
      Return planned order states.
    """
698 699 700
    return self._getPortalGroupedStateList('planned_order') or \
           self._getPortalConfiguration('portal_planned_order_state_list')

701 702 703 704 705 706
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalReservedInventoryStateList')
  def getPortalReservedInventoryStateList(self):
    """
      Return reserved inventory states.
    """
707
    return self._getPortalGroupedStateList('reserved_inventory') or \
708
        self._getPortalConfiguration('portal_reserved_inventory_state_list')
709

710 711 712 713 714 715
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalFutureInventoryStateList')
  def getPortalFutureInventoryStateList(self):
    """
      Return future inventory states.
    """
716 717 718
    return self._getPortalGroupedStateList('future_inventory') or \
           self._getPortalConfiguration('portal_future_inventory_state_list')

719
  security.declareProtected(Permissions.AccessContentsInformation,
720
                          'getPortalUpdatableAmortisationTransactionStateList')
721 722
  def getPortalUpdatableAmortisationTransactionStateList(self):
    """
723
      Return states when Amortisation Transaction can be updated
724
      by amortisation_transaction_builder.
725
    """
726 727
    return self._getPortalConfiguration(
        'portal_updatable_amortisation_transaction_state_list')
728

729 730 731 732 733 734
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalColumnBaseCategoryList')
  def getPortalColumnBaseCategoryList(self):
    """
      Return column base categories.
    """
735 736 737
    return self._getPortalGroupedCategoryList('column') or \
           self._getPortalConfiguration('portal_column_base_category_list')

738 739 740 741 742 743
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalLineBaseCategoryList')
  def getPortalLineBaseCategoryList(self):
    """
      Return line base categories.
    """
744 745 746
    return self._getPortalGroupedCategoryList('line') or \
           self._getPortalConfiguration('portal_line_base_category_list')

747 748 749 750 751 752
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalTabBaseCategoryList')
  def getPortalTabBaseCategoryList(self):
    """
      Return tab base categories.
    """
753 754 755
    return self._getPortalGroupedCategoryList('tab') or \
           self._getPortalConfiguration('portal_tab_base_category_list')

756 757 758 759
  def getPortalDefaultGapRoot(self):
    """
      Return the Accounting Plan to use by default (return the root node)
    """
760
    LOG('ERP5Site', 0,
761 762 763
        'getPortalDefaultGapRoot is deprecated; ' \
        'use portal_preferences.getPreferredAccountingTransactionGap instead.')

764 765
    return self.portal_preferences.getPreferredAccountingTransactionGap() or \
           self._getPortalConfiguration('portal_default_gap_root')
766

767 768 769 770
  def getPortalAccountingMovementTypeList(self) :
    """
      Return accounting movement type list.
    """
771
    return self._getPortalGroupedTypeList('accounting_movement') or \
772
        self._getPortalConfiguration('portal_accounting_movement_type_list')
773

774 775 776 777
  def getPortalAccountingTransactionTypeList(self) :
    """
      Return accounting transaction movement type list.
    """
778
    return self._getPortalGroupedTypeList('accounting_transaction') or \
779
      self._getPortalConfiguration('portal_accounting_transaction_type_list')
780

781 782 783 784
  def getPortalAssignmentBaseCategoryList(self):
    """
      Return List of category values to generate security groups.
    """
785
    return self._getPortalGroupedCategoryList('assignment') or \
786
        self._getPortalConfiguration('portal_assignment_base_category_list')
787

788 789
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalTicketTypeList')
790 791 792 793 794
  def getPortalTicketTypeList(self):
    """
    Return ticket types.
    """
    return self._getPortalGroupedTypeList('ticket')
795

796 797
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalEventTypeList')
798 799 800 801 802
  def getPortalEventTypeList(self):
    """
    Return event types.
    """
    return self._getPortalGroupedTypeList('event')
803

804 805 806 807 808 809 810 811
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getPortalDivergenceTesterTypeList')
  def getPortalDivergenceTesterTypeList(self):
    """
    Return divergence tester types.
    """
    return self._getPortalGroupedTypeList('divergence_tester')

812 813
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getDefaultModuleId')
814 815 816 817 818 819 820 821 822 823
  def getDefaultModuleId(self, portal_type, default=MARKER):
    """
      Return default module id where a object with portal_type can
      be created.
    """
    # Very dummy method, but it works with today name convention.
    module_name = portal_type.lower().replace(' ','_')
    portal_object = self
    if not hasattr(portal_object, module_name):
      module_name += '_module'
824
      if not hasattr(portal_object, module_name):
825 826 827
        if default is not MARKER:
          return default
        LOG('ERP5Site, getDefaultModuleId', 0,
828 829 830 831
            'Unable to find default module for portal_type: %s' % \
                portal_type)
        raise ValueError, 'Unable to find module for portal_type: %s' % \
                          portal_type
832 833
    return module_name

834 835
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getDefaultModule')
836 837 838 839 840
  def getDefaultModule(self, portal_type):
    """
      Return default module where a object with portal_type can be created
    """
    return getattr(self, self.getDefaultModuleId(portal_type), None)
841

842 843 844 845 846 847 848 849 850
  security.declareProtected(Permissions.AddPortalContent, 'newContent')
  def newContent(self, id=None, portal_type=None, immediate_reindex=0, **kw):
    """
      Creates a new content
    """
    if id is None:
      raise ValueError, 'The id should not be None'
    if portal_type is None:
      raise ValueError, 'The portal_type should not be None'
851 852 853 854
    self.portal_types.constructContent(type_name=portal_type,
                                       container=self,
                                       id=id,
                                       ) # **kw) removed due to CMF bug
855
    new_instance = self[id]
856 857 858 859
    if kw is not None:
      new_instance._edit(force_update=1, **kw)
    if immediate_reindex:
      new_instance.immediateReindexObject()
860 861
    return new_instance

862
  def log(self,description,content=''):
863
    """
864
    Put a log message
865
    """
866 867 868 869 870 871 872 873 874
    if content=='': # allow for content only while keeping interface
        description,content=content,description
    st=traceback.extract_stack()
    head=[]
    for frame in st[-2:-6:-1]: # assume no deep nesting in Script (Python)
        if frame[0]=='Script (Python)': # does anybody log from ZPT or dtml?
            head.append('%s, %d' % (frame[2],frame[1]))
    head=' -> '.join(head)
    description='%s: %s' % (head,description)
875 876
    LOG(description, 0, content)

877

Jean-Paul Smets's avatar
Jean-Paul Smets committed
878 879 880 881
Globals.InitializeClass(ERP5Site)

class ERP5Generator(PortalGenerator):

882
  klass = ERP5Site
883

884 885 886 887 888 889 890
  def getBootstrapDirectory(self):
    """
      Return the name of the bootstrap directory
    """
    product_path = package_home(globals())
    return os.path.join(product_path, 'bootstrap')

891 892 893
  def create(self,
             parent,
             id,
894
             create_userfolder,
895
             erp5_sql_connection_type,
896 897 898 899 900 901 902 903
             erp5_sql_connection_string,
             erp5_sql_deferred_connection_type,
             erp5_sql_deferred_connection_string,
             cmf_activity_sql_connection_type,
             cmf_activity_sql_connection_string,
             create_activities=1,
             reindex=1,
             **kw):
904 905
    id = str(id)
    portal = self.klass(id=id)
906 907
    # Make sure reindex will not be called until business templates
    # will be installed
908
    setattr(portal, 'isIndexable', 0)
909 910 911
    parent._setObject(id, portal)
    # Return the fully wrapped object.
    p = parent.this()._getOb(id)
912
    p._setProperty('erp5_sql_connection_type',
913
                   erp5_sql_connection_type, 'string')
914
    p._setProperty('erp5_sql_connection_string',
915
                   erp5_sql_connection_string, 'string')
916
    p._setProperty('erp5_sql_deferred_connection_type',
917
                   erp5_sql_deferred_connection_type, 'string')
918
    p._setProperty('erp5_sql_deferred_connection_string',
919
                   erp5_sql_deferred_connection_string, 'string')
920
    p._setProperty('cmf_activity_sql_connection_type',
921
                   cmf_activity_sql_connection_type, 'string')
922
    p._setProperty('cmf_activity_sql_connection_string',
923 924
                   cmf_activity_sql_connection_string, 'string')
    # XXX hardcoded charset
925
    p._setProperty('management_page_charset', 'UTF-8', 'string')
926 927
    self.setup(p, create_userfolder, create_activities=create_activities,
        reindex=reindex, **kw)
928
    p.portal_caches.updateCache()
929 930 931 932
    return p

  def setupLastTools(self, p, **kw):
    """
933 934 935
    Set up finals tools
    We want to set the activity tool only at the end to
    make sure that we do not put un the queue the full reindexation
936 937
    """
    # Add Activity Tool
938
    if kw.has_key('create_activities') and int(kw['create_activities'])==1:
939 940 941 942 943 944 945 946 947 948 949
      if not p.hasObject('portal_activities'):
        addTool = p.manage_addProduct['CMFActivity'].manage_addTool
        addTool('CMF Activity Tool', None) # Allow user to select active/passive
      # Initialize Activities
      portal_activities = getToolByName(p, 'portal_activities', None)
      if portal_activities is not None:
        if kw.get('update', 0):
          keep = 1
        else:
          keep = 0
        portal_activities.manageClearActivities(keep=keep)
950

951 952
  def setupTemplateTool(self, p, **kw):
    """
953
    Setup the Template Tool. Security must be set strictly.
954 955 956 957 958 959 960 961 962 963
    """
    addTool = p.manage_addProduct['ERP5'].manage_addTool
    addTool('ERP5 Template Tool', None)
    context = p.portal_templates
    permission_list = context.possible_permissions()
    for permission in permission_list:
      context.manage_permission(permission, ['Manager'], 0)

  def setupTools(self, p,**kw):
    """
964
    Set up initial tools.
965 966 967 968
    """
    if not 'portal_actions' in p.objectIds():
      PortalGenerator.setupTools(self, p)

969
    # It is better to remove portal_catalog
970
    # which is ZCatalog as soon as possible,
971
    # because the API is not the completely same as ERP5Catalog,
972
    # and ZCatalog is useless for ERP5 after all.
973 974
    update = kw.get('update', 0)
    portal_catalog = getToolByName(p, 'portal_catalog', None)
975 976 977
    if portal_catalog is not None and \
       portal_catalog.meta_type != 'ZSQLCatalog' and \
       not update:
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
      p._delObject('portal_catalog')

    # Add CMF Report Tool
    if not p.hasObject('portal_report'):
      addTool = p.manage_addProduct['CMFReportTool'].manage_addTool
      addTool('CMF Report Tool', None)

    # Add ERP5 Tools
    addTool = p.manage_addProduct['ERP5'].manage_addTool
    if not p.hasObject('portal_categories'):
      addTool('ERP5 Categories', None)
    if not p.hasObject('portal_rules'):
      addTool('ERP5 Rule Tool', None)
    if not p.hasObject('portal_ids'):
      addTool('ERP5 Id Tool', None)
    if not p.hasObject('portal_simulation'):
      addTool('ERP5 Simulation Tool', None)
    if not p.hasObject('portal_templates'):
      self.setupTemplateTool(p)
    if not p.hasObject('portal_trash'):
      addTool('ERP5 Trash Tool', None)
    if not p.hasObject('portal_alarms'):
      addTool('ERP5 Alarm Tool', None)
    if not p.hasObject('portal_domains'):
      addTool('ERP5 Domain Tool', None)
    if not p.hasObject('portal_deliveries'):
      addTool('ERP5 Delivery Tool', None)
    if not p.hasObject('portal_orders'):
      addTool('ERP5 Order Tool', None)
1007 1008
    if not p.hasObject('portal_tests'):
      addTool('ERP5 Test Tool', None)
1009 1010 1011
    
    # Add ERP5Type Tool
    addTool = p.manage_addProduct['ERP5Type'].manage_addTool
1012 1013
    if not p.hasObject('portal_caches'):
      addTool('ERP5 Cache Tool', None)
1014

1015 1016 1017 1018 1019 1020
    try:
      addTool = p.manage_addProduct['ERP5Subversion'].manage_addTool
      if not p.hasObject('portal_subversion'):
        addTool('ERP5 Subversion Tool', None)
    except AttributeError:
      pass
1021

1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036
    # Add ERP5Type Tools
    addTool = p.manage_addProduct['ERP5Type'].manage_addTool
    if not p.hasObject('portal_classes'):
      if allowClassTool():
        addTool('ERP5 Class Tool', None)
      else:
        addTool('ERP5 Dummy Class Tool', None)

    # Add ERP5 SQL Catalog Tool
    addTool = p.manage_addProduct['ERP5Catalog'].manage_addTool
    if not p.hasObject('portal_catalog'):
      addTool('ERP5 Catalog', None)
    # Add Default SQL connection
    if p.erp5_sql_connection_type == 'Z MySQL Database Connection':
      if not p.hasObject('erp5_sql_connection'):
1037 1038
        addSQLConnection = p.manage_addProduct['ZMySQLDA'].\
                                     manage_addZMySQLConnection
1039 1040
        addSQLConnection('erp5_sql_connection',
                         'ERP5 SQL Server Connection',
1041
                         p.erp5_sql_connection_string)
1042 1043
    elif p.erp5_sql_connection_type == 'Z Gadfly':
      pass
1044 1045

    # Add Deferred SQL Connections
1046 1047
    if p.erp5_sql_deferred_connection_type == \
        'Z MySQL Deferred Database Connection':
1048
      if not p.hasObject('erp5_sql_deferred_connection'):
1049 1050
        addSQLConnection = p.manage_addProduct['ZMySQLDDA'].\
            manage_addZMySQLDeferredConnection
1051 1052
        addSQLConnection('erp5_sql_deferred_connection',
                         'ERP5 SQL Server Deferred Connection',
1053
                         p.erp5_sql_deferred_connection_string)
1054 1055 1056 1057
    elif p.erp5_sql_deferred_connection_type == 'Z Gadfly':
      pass

    # Add Activity SQL Connections
1058 1059
    if p.cmf_activity_sql_connection_type == 'Z MySQL Database Connection':
      if not p.hasObject('cmf_activity_sql_connection'):
1060 1061
        addSQLConnection = p.manage_addProduct['ZMySQLDA'].\
                                     manage_addZMySQLConnection
1062 1063
        addSQLConnection('cmf_activity_sql_connection',
                         'CMF Activity SQL Server Connection',
1064
                         p.cmf_activity_sql_connection_string)
1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
      # Warning : This transactionless connection is created with
      # the activity connection string and not the catalog's because
      # it's not compatible with the hot reindexing feature.
      # Though, it has nothing to do with activities.
      # The only difference compared to activity connection is the
      # minux prepended to the conneciton string.
      if not p.hasObject('erp5_sql_transactionless_connection'):
        addSQLConnection = p.manage_addProduct['ZMySQLDA'].\
                                     manage_addZMySQLConnection
        addSQLConnection('erp5_sql_transactionless_connection',
                         'ERP5 Transactionless SQL Server Connection',
Vincent Pelletier's avatar
Typo.  
Vincent Pelletier committed
1076
                         '-%s' % p.cmf_activity_sql_connection_string)
1077 1078
    elif p.cmf_activity_sql_connection_type == 'Z Gadfly':
      pass
1079

1080
    portal_catalog = getToolByName(p, 'portal_catalog')
1081
    if (not update) and (not portal_catalog.getSQLCatalog('erp5_mysql')):
1082 1083 1084
      # Add a empty SQL Catalog, which will be filled when installing
      # erp5_core business template
      portal_catalog.manage_addProduct['ZSQLCatalog'].manage_addSQLCatalog(
1085
          'erp5_mysql', 'ERP5/MySQL')
1086
      portal_catalog.default_sql_catalog_id = 'erp5_mysql'
1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104

    # Add ERP5Form Tools
    addTool = p.manage_addProduct['ERP5Form'].manage_addTool
    if not p.hasObject('portal_selections'):
      addTool('ERP5 Selections', None)
    if not p.hasObject('portal_preferences'):
      addTool('ERP5 Preference Tool', None)

    # Add ERP5SyncML Tools
    addTool = p.manage_addProduct['ERP5SyncML'].manage_addTool
    if not p.hasObject('portal_synchronizations'):
      addTool('ERP5 Synchronizations', None)

    # Add Message Catalog
    if not 'Localizer' in p.objectIds():
      addLocalizer = p.manage_addProduct['Localizer'].manage_addLocalizer
      addLocalizer('', ('en',))
    localizer = getToolByName(p, 'Localizer')
1105 1106
    addMessageCatalog = localizer.manage_addProduct['Localizer']\
                                      .manage_addMessageCatalog
1107 1108 1109
    if 'erp5_ui' not in localizer.objectIds():
      if 'default' in localizer.objectIds():
        localizer.manage_delObjects('default')
1110 1111 1112
      addMessageCatalog('default', 'ERP5 Localized Messages', ('en',))
      addMessageCatalog('erp5_ui', 'ERP5 Localized Interface', ('en',))
      addMessageCatalog('erp5_content', 'ERP5 Localized Content', ('en',))
1113 1114 1115 1116


  def setupMembersFolder(self, p):
    """
1117
    ERP5 is not a CMS
1118 1119 1120 1121 1122
    """
    pass

  def setupDefaultSkins(self, p):
    from Products.CMFCore.DirectoryView import addDirectoryViews
1123
    from Products.CMFDefault  import cmfdefault_globals
1124
    from Products.CMFActivity import cmfactivity_globals
Jérome Perrin's avatar
Jérome Perrin committed
1125 1126 1127 1128 1129
    try:
      from Products.FCKeditor   import fckeditor_globals
      have_fckeditor = 1
    except:
      have_fckeditor = 0
1130 1131 1132
    ps = getToolByName(p, 'portal_skins')
    addDirectoryViews(ps, 'skins', cmfdefault_globals)
    addDirectoryViews(ps, 'skins', cmfactivity_globals)
Jérome Perrin's avatar
Jérome Perrin committed
1133 1134
    if have_fckeditor:
      addDirectoryViews(ps, 'skins', fckeditor_globals)
1135 1136
    ps.manage_addProduct['OFSP'].manage_addFolder(id='external_method')
    ps.manage_addProduct['OFSP'].manage_addFolder(id='custom')
1137
    # Set the 'custom' layer a high priority, so it remains the first
1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150
    #   layer when installing new business templates.
    ps['custom'].manage_addProperty("business_template_skin_layer_priority", 100.0, "float")
    skin_folder_list = [ 'custom'
                       , 'external_method'
                       , 'activity'
                       , 'zpt_content'
                       , 'zpt_generic'
                       , 'zpt_control'
                       , 'content'
                       , 'generic'
                       , 'control'
                       , 'Images'
                       ]
Jérome Perrin's avatar
Jérome Perrin committed
1151 1152
    if have_fckeditor:
      skin_folder_list.insert(1, 'fckeditor')
1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165
    skin_folders = ', '.join(skin_folder_list)
    ps.addSkinSelection( 'View'
                       , skin_folders
                       , make_default = 1
                       )
    ps.addSkinSelection( 'Print'
                       , skin_folders
                       , make_default = 0
                       )
    ps.addSkinSelection( 'CSV'
                       , skin_folders
                       , make_default = 0
                       )
1166 1167 1168 1169
    p.setupCurrentSkin()

  def setupWorkflow(self, p):
    """
1170
    Set up workflows for business templates
1171 1172 1173
    """
    tool = getToolByName(p, 'portal_workflow', None)
    if tool is None:
Jérome Perrin's avatar
Jérome Perrin committed
1174
      return
1175
    for wf_id in ('business_template_building_workflow',
1176
                  'business_template_installation_workflow'):
1177 1178 1179
      if wf_id in tool.objectIds():
        tool.manage_delObjects([wf_id])
    bootstrap_dir = self.getBootstrapDirectory()
1180 1181 1182
    business_template_building_workflow = os.path.join(
                                 bootstrap_dir,
                                 'business_template_building_workflow.xml')
1183
    tool._importObjectFromFile(business_template_building_workflow)
1184 1185 1186
    business_template_installation_workflow = os.path.join(
                                 bootstrap_dir,
                                 'business_template_installation_workflow.xml')
1187 1188
    tool._importObjectFromFile(business_template_installation_workflow)
    tool.setChainForPortalTypes( ( 'Business Template', ),
1189 1190
                                 ( 'business_template_building_workflow',
                                   'business_template_installation_workflow' ) )
1191 1192 1193

  def setupIndex(self, p, **kw):
    # Make sure all tools and folders have been indexed
1194
    if not kw.get('reindex', 1):
1195 1196 1197 1198
      return
    skins_tool = getToolByName(p, 'portal_skins', None)
    if skins_tool is None:
      return
1199
    # When no SQL connection was define on the site,
1200 1201 1202 1203 1204 1205
    # we don't want to make it crash
    if p.erp5_sql_connection_type is not None:
      setattr(p, 'isIndexable', 1)
      portal_catalog = p.portal_catalog
      portal_catalog.manage_catalogClear()
      skins_tool["erp5_core"].ERP5Site_reindexAll()
1206

1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226
  def setupUserFolder(self, p):
    # We use if possible ERP5Security, then NuxUserGroups
    try:
      from Products import ERP5Security
      from Products import PluggableAuthService
    except ImportError:
      ERP5Security = None
      try:
        import Products.NuxUserGroups
        withnuxgroups = 1
      except ImportError:
        withnuxgroups = 0
    if ERP5Security is not None:
      # Use Pluggable Auth Service instead of the standard acl_users.
      p.manage_addProduct['PluggableAuthService'].addPluggableAuthService()
      # Add legacy ZODB support
      p.acl_users.manage_addProduct['PluggableAuthService'].addZODBUserManager('zodb_users')
      p.acl_users.manage_addProduct['PluggableAuthService'].addZODBGroupManager('zodb_groups')
      p.acl_users.manage_addProduct['PluggableAuthService'].addZODBRoleManager('zodb_roles')
      # Add CMF Portal Roles
1227
      #XXX Maybe it will be no longer required once PAS is the standard
1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259
      p.acl_users.zodb_roles.addRole('Member')
      p.acl_users.zodb_roles.addRole('Reviewer')
      # Register ZODB Interface
      p.acl_users.zodb_users.manage_activateInterfaces(('IAuthenticationPlugin',
                                                    'IUserEnumerationPlugin','IUserAdderPlugin'))
      p.acl_users.zodb_groups.manage_activateInterfaces(('IGroupsPlugin',
                                                    'IGroupEnumerationPlugin'))
      p.acl_users.zodb_roles.manage_activateInterfaces(('IRoleEnumerationPlugin',
                                                    'IRolesPlugin', 'IRoleAssignerPlugin'))
      # Add ERP5UserManager
      p.acl_users.manage_addProduct['ERP5Security'].addERP5UserManager('erp5_users')
      p.acl_users.manage_addProduct['ERP5Security'].addERP5GroupManager('erp5_groups')
      p.acl_users.manage_addProduct['ERP5Security'].addERP5RoleManager('erp5_roles')
      # Register ERP5UserManager Interface
      p.acl_users.erp5_users.manage_activateInterfaces(('IAuthenticationPlugin',
                                                        'IUserEnumerationPlugin',))
      p.acl_users.erp5_groups.manage_activateInterfaces(('IGroupsPlugin',))
      p.acl_users.erp5_roles.manage_activateInterfaces(('IRolesPlugin',))
    elif withnuxgroups:
      # NuxUserGroups user folder
      p.manage_addProduct['NuxUserGroups'].addUserFolderWithGroups()
    else:
      # Standard user folder
      PortalGenerator.setupUserFolder(self, p)

  def setupPermissions(self, p):
    permission_dict = {
      'Access Transient Objects'     : ('Manager', 'Anonymous'),
      'Access contents information'  : ('Manager', 'Member', 'Anonymous'),
      'Access future portal content' : ('Manager', 'Reviewer'),
      'Access session data'          : ('Manager', 'Anonymous'),
      'AccessContentsInformation'    : ('Manager', 'Member'),
1260
      'Change local roles'           : ('Manager', ),
1261 1262 1263 1264 1265 1266
      'Add portal content'           : ('Manager', 'Owner'),
      'Add portal folders'           : ('Manager', 'Owner'),
      'Delete objects'               : ('Manager', 'Owner'),
      'FTP access'                   : ('Manager', 'Owner'),
      'List folder contents'         : ('Manager', 'Member'),
      'List portal members'          : ('Manager', 'Member'),
1267
      'List undoable changes'        : ('Manager', ),
1268 1269 1270 1271 1272
      'Manage properties'            : ('Manager', 'Owner'),
      'Modify portal content'        : ('Manager', 'Owner'),
      'Reply to item'                : ('Manager', 'Member'),
      'Review portal content'        : ('Manager', 'Reviewer'),
      'Search ZCatalog'              : ('Manager', 'Member'),
1273
      'Set own password'             : ('Manager', ),
1274 1275
      'Set own properties'           : ('Manager', 'Member'),
      'Undo changes'                 : ('Manager', 'Owner'),
1276
      'View'                         : ('Manager', 'Member',
1277
                                        'Owner', 'Anonymous'),
1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288
      'View management screens'      : ('Manager', 'Owner')
    }

    for permission in p.ac_inherited_permissions(1):
      name = permission[0]
      role_list = permission_dict.get(name, ('Manager',))
      p.manage_permission(name, roles=role_list, acquire=0)

  def setup(self, p, create_userfolder, **kw):
    update = kw.get('update', 0)

1289 1290 1291
    if getattr(p, 'setDefaultSorting', None) is not None:
      p.setDefaultSorting('id', 0)

1292 1293 1294 1295 1296
    self.setupTools(p, **kw)

    if not p.hasObject('MailHost'):
      self.setupMailHost(p)

Jérome Perrin's avatar
Jérome Perrin committed
1297 1298
    if create_userfolder and not p.hasObject('acl_users'):
      self.setupUserFolder(p)
1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325

    if not p.hasObject('cookie_authentication'):
      self.setupCookieAuth(p)

    if 'Member' not in getattr(p, '__ac_roles__', ()):
      self.setupRoles(p)

    if not update:
      self.setupPermissions(p)
      self.setupDefaultSkins(p)

    # Finish setup
    if not p.hasObject('Members'):
      self.setupMembersFolder(p)

    # ERP5 Design Choice is that all content should be user defined
    # Content is disseminated through business templates
    self.setupBusinessTemplate(p)

    if not p.hasObject('content_type_registry'):
      self.setupMimetypes(p)
    if not update:
      self.setupWorkflow(p)

    if not update:
      self.setupERP5Core(p,**kw)

1326 1327
    self.setupLastTools(p, **kw)

1328 1329 1330 1331 1332 1333 1334
    # Make sure tools are cleanly indexed with a uid before creating children
    # XXX for some strange reason, member was indexed 5 times
    if not update:
      self.setupIndex(p, **kw)

  def setupBusinessTemplate(self,p):
    """
1335
    Install the portal_type of Business Template
1336 1337 1338 1339 1340 1341 1342 1343 1344
    """
    tool = getToolByName(p, 'portal_types', None)
    if tool is None:
      return
    if 'Business Template' not in tool.objectIds():
      t = BusinessTemplate.factory_type_information
      ti = apply(ERP5TypeInformation, (), t)
      tool._setObject(t['id'], ti)

1345
  def setupERP5Core(self,p,**kw):
1346
    """
1347
    Install the core part of ERP5
1348 1349 1350 1351 1352 1353
    """
    template_tool = getToolByName(p, 'portal_templates', None)
    if template_tool is None:
      return
    if template_tool.getInstalledBusinessTemplate('erp5_core') is None:
      bootstrap_dir = self.getBootstrapDirectory()
1354
      for bt in ('erp5_core', 'erp5_xhtml_style'):
1355 1356 1357 1358 1359 1360 1361
        template = os.path.join(bootstrap_dir, bt)
        if not os.path.exists(template):
          template = os.path.join(bootstrap_dir, '%s.bt5' % bt)

        id = template_tool.generateNewId()
        template_tool.download(template, id=id)
        template_tool[id].install(**kw)