WebSite.py 7.46 KB
Newer Older
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1 2 3 4 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
##############################################################################
#
# Copyright (c) 2002-2006 Nexedi SARL and Contributors. All Rights Reserved.
#
# 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.
#
##############################################################################

from AccessControl import ClassSecurityInfo
from AccessControl import getSecurityManager
from Acquisition import aq_base
from Products.CMFCore.utils import getToolByName
32 33
from Products.ERP5Type import Permissions, PropertySheet,\
                              Constraint, Interface, Cache
Jean-Paul Smets's avatar
Jean-Paul Smets committed
34 35 36 37
from Products.ERP5.Document.Domain import Domain
from Acquisition import ImplicitAcquisitionWrapper, aq_base, aq_inner
from Products.ERP5Type.Base import TempBase
from Globals import get_request
38
from Persistence import Persistent
39 40
from Products.CMFCore.utils import UniqueObject, _checkPermission,\
                                   _getAuthenticatedUser
Jean-Paul Smets's avatar
Jean-Paul Smets committed
41 42
from AccessControl.SecurityManagement import getSecurityManager
from AccessControl.User import emergency_user
43 44 45
from AccessControl.SecurityManagement import newSecurityManager,\
                                             setSecurityManager
from ZPublisher import BeforeTraverse
Jean-Paul Smets's avatar
Jean-Paul Smets committed
46 47 48

from zLOG import LOG

49
website_key = 'web_site_value'
50
class WebSiteTraversalHook(Persistent):
51 52 53 54
  """This is used by WebSite to rewrite URLs in such way
  that once a user gets into a Web Site object, all
  documents referenced by the web site are accessed
  through the web site rather than directly.
55
  We inherit for persistent, so that pickle mechanism ignores _v_request
56 57 58 59 60 61 62 63 64 65
  """

  def _physicalPathToVirtualPath(self, path):
    """
      Remove the path to the VirtualRoot from a physical path
      and add the path to the WebSite is any
    """
    if type(path) is type(''):
      path = path.split( '/')

66
    website_path = self._v_request.get(website_key, None)
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
    if website_path:
      website_path = tuple(website_path)    # Make sure all path are tuples
      path = tuple(path)                    # Make sure all path are tuples
      # Search for the common part index
      # XXX more testing should be added to check
      # if the URL is the kind of URL which is a Web Site
      # XXX more support required for ignore_layout
      common_index = 0
      i = 0
      path_len = len(path)
      for name in website_path:
        if i >= path_len: break
        if path[i] == name:
          common_index = i
        i += 1
      # Insert the web site path after the common part of the path
      if path_len > common_index + 1:
        path = website_path + path[common_index + 1:]

86
    rpp = self._v_request.other.get('VirtualRootPhysicalPath', ('',))
87 88 89 90 91 92 93
    i = 0
    for name in rpp[:len(path)]:
      if path[i] == name:
        i = i + 1
      else:
        break
    return path[i:]
94
  
95 96 97
  def __call__(self, container, request):
    """Each time we are traversed, we patch the request instance with our
    rewritted version of physicalPathToVirtualPath"""
98
    self._v_request = request
99 100
    request.physicalPathToVirtualPath = self._physicalPathToVirtualPath

Jean-Paul Smets's avatar
Jean-Paul Smets committed
101 102
Domain_getattr = Domain.inheritedAttribute('__getattr__')

103 104 105
# Use a request key to store access attributes and prevent infinite recursion
cache_key = 'web_site_aq_cache'

Jean-Paul Smets's avatar
Jean-Paul Smets committed
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
class WebSite(Domain):
    """
      A Web Site root class. This class is used by ERP5 Commerce
      to define the root of an eCommerce site.

      WARNING:
        - Z Catalog Search permission must be set for Anonymous

      TODO:
        - accelerate document lookup by caching acceptable keys
        - fix missing REQUEST information in aq_dynamic documents
    """
    # CMF Type Definition
    meta_type = 'ERP5 Web Site'
    portal_type = 'Web Site'
    isPortalContent = 1
    isRADContent = 1

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

    # Default Properties
    property_sheets = ( PropertySheet.Base
                      , PropertySheet.XMLObject
                      , PropertySheet.CategoryCore
                      , PropertySheet.DublinCore
                      , PropertySheet.WebSite
                      )

    def _aq_dynamic(self, name):
      """
        Try to find a suitable document based on the
        web site local naming policies as defined by
140 141
        the WebSite_getDocument script
      """
142
      request = self.REQUEST
143 144
      # Register current web site physical path for later URL generation
      if not request.has_key(website_key): request[website_key] = self.getPhysicalPath()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
145 146 147 148
      # First let us call the super method
      dynamic = Domain._aq_dynamic(self, name)
      if dynamic is not None :
        return dynamic
149
      # Do some optimisation here for names which can not be names of documents
Jean-Paul Smets's avatar
Jean-Paul Smets committed
150 151
      if name.startswith('_') or name.startswith('portal_')\
          or name.startswith('aq_') or name.startswith('selection_') \
152 153
          or name.startswith('sort-') or name == 'getLayout' \
          or name == 'getListItemUrl' or name.startswith('WebSite_'):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
154
        return None
155 156 157 158
      if not request.has_key(cache_key):
        request[cache_key] = {}
      elif request[cache_key].has_key(name):
        return request[cache_key][name]
Jean-Paul Smets's avatar
Jean-Paul Smets committed
159 160 161 162
      try:
        portal = self.getPortalObject()
        # Use the webmaster identity to find documents
        user = portal.acl_users.getUserById(self.getWebmaster())
163 164 165
        if user is not None:
          old_manager = getSecurityManager()
          newSecurityManager(get_request(), user)
166
        document = self.WebSite_getDocument(portal, name)
167 168 169
        request[cache_key][name] = document
        if user is not None:
          setSecurityManager(old_manager)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
170 171
      except:
        # Cleanup non recursion dict in case of exception
172 173
        if request[cache_key].has_key(name):
          del request[cache_key][name]
Jean-Paul Smets's avatar
Jean-Paul Smets committed
174
        raise
175
      return document
176

177 178 179 180
    security.declarePrivate( 'manage_beforeDelete' )
    def manage_beforeDelete(self, item, container):
      if item is self:
        handle = self.meta_type + '/' + self.getId()
181
        BeforeTraverse.unregisterBeforeTraverse(item, handle)
182 183 184 185 186 187
      Domain.manage_beforeDelete(self, item, container)

    security.declarePrivate( 'manage_afterAdd' )
    def manage_afterAdd(self, item, container):
      if item is self:
        handle = self.meta_type + '/' + self.getId()
188
        BeforeTraverse.registerBeforeTraverse(item,
189 190 191
                                              WebSiteTraversalHook(),
                                              handle)
      Domain.manage_afterAdd(self, item, container)
192