CategoryTool.py 7.44 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
#
# WARNING: This program as such is intended to be used by professional
7
# programmers who take the whole responsibility of assessing all potential
Jean-Paul Smets's avatar
Jean-Paul Smets committed
8 9
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
10
# guarantees and support are strongly adviced to contract a Free Software
Jean-Paul Smets's avatar
Jean-Paul Smets committed
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################

"""\
Vincent Pelletier's avatar
Vincent Pelletier committed
30
ERP5 portal_categories tool.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
31 32
"""

33
from Products.CMFCategory.CategoryTool import CategoryTool as CMFCategoryTool
34
from Products.ERP5Type.Tool.BaseTool import BaseTool
35
from AccessControl import ClassSecurityInfo
36
from Acquisition import aq_base
37
from Products.ERP5Type.Globals import InitializeClass
38
from Products.ERP5Type import Permissions
39
from Products.ERP5Type.Core.Folder import OFS_HANDLER
40
from Products.ERP5Type.CopySupport import CopyContainer
41
from Products.CMFCore.utils import getToolByName
42
from Products.ERP5Type.Cache import caching_instance_method
43
from Products.ERP5Type.dynamic import portal_type_class
Jean-Paul Smets's avatar
Jean-Paul Smets committed
44 45 46

from zLOG import LOG

47
class CategoryTool(CMFCategoryTool, BaseTool):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
48 49 50 51 52 53 54
    """
      The CategoryTool object is the placeholder for all methods
      and algorithms related to categories and relations in ERP5.
    """

    id              = 'portal_categories'
    meta_type       = 'ERP5 Categories'
55
    portal_type     = 'Category Tool'
56
    title           = 'Categories'
Jean-Paul Smets's avatar
Jean-Paul Smets committed
57
    allowed_types   = ( 'ERP5 Base Category',)
58
    _folder_handler = OFS_HANDLER
Jean-Paul Smets's avatar
Jean-Paul Smets committed
59 60 61 62

    # Declarative Security
    security = ClassSecurityInfo()

63 64
    objectValues = BaseTool.objectValues

Jean-Paul Smets's avatar
Jean-Paul Smets committed
65 66 67
    # Filter content (ZMI))
    def filtered_meta_types(self, user=None):
        # Filters the list of available meta types.
68
        #all = CMFCategoryTool.inheritedAttribute('filtered_meta_types')(self)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
69 70 71 72 73 74
        meta_types = []
        for meta_type in self.all_meta_types():
            if meta_type['name'] in self.allowed_types:
                meta_types.append(meta_type)
        return meta_types

75 76
    # patch, so that we are able to add the BaseCategory
    allowedContentTypes = BaseTool.allowedContentTypes
77
    getVisibleAllowedContentTypeList = BaseTool.getVisibleAllowedContentTypeList
78

79 80 81
    # Override this method to resolve an inheritance problem.
    def _verifyObjectPaste(self, *args, **kw):
      return BaseTool._verifyObjectPaste(self, *args, **kw)
82 83 84

    all_meta_types = BaseTool.all_meta_types

Sebastien Robin's avatar
Sebastien Robin committed
85 86 87 88
    security.declareProtected(Permissions.View, 'hasContent')
    def hasContent(self,id):
      return id in self.objectIds()

89
    security.declareProtected(Permissions.AccessContentsInformation, 'getBaseCategoryDict')
90 91 92 93
    @caching_instance_method(
      id='portal_categories.getBaseCategoryDict',
      cache_factory='erp5_content_long',
      cache_id_generator=lambda *a: portal_type_class.last_sync)
94 95 96 97 98 99
    def getBaseCategoryDict(self):
      """
        Cached method to which resturns a dict with category names as keys, and None as values.
        This allows to search for an element existence in the list faster.
        ie: if x in self.getPortalObject().portal_categories.getBaseCategoryDict()
      """
100
      return dict.fromkeys(self.getBaseCategoryList(), None)
101

102 103
    def updateRelatedContent(self, context,
                             previous_category_url, new_category_url):
104 105 106 107 108
      """Updates categories of related objects and predicate membership.
          o context: the moved object
          o previous_category_url: the related url of this object before
            the move
          o new_category_url: the related url of the object after the move
109

110
      TODO: make this method resist to very large updates (ie. long transaction)
111
      """
112
      portal_catalog = getToolByName(context, 'portal_catalog')
113 114 115
      activate_kw = {'tag':'%s_updateRelatedContent' % context.getPath()}

      # udpate category related objects
116
      kw = {'category.category_uid': context.getUid(), 'limit': None}
117 118 119 120 121 122 123 124 125 126 127 128
      for related_object in portal_catalog(**kw):
        related_object = related_object.getObject()
        category_list = []
        for category in related_object.getCategoryList():
          new_category = self.updateRelatedCategory(category,
                                                    previous_category_url,
                                                    new_category_url)
          category_list.append(new_category)
        related_object.edit(categories=category_list,
                            activate_kw=activate_kw)

      # udpate all predicates membership
129
      kw = {'predicate_category.category_uid': context.getUid(), 'limit': None}
130 131
      for predicate in portal_catalog(**kw):
        predicate = predicate.getObject()
132 133
        membership_list = []
        for category in predicate.getMembershipCriterionCategoryList():
134 135 136
          new_category = self.updateRelatedCategory(category,
                                                    previous_category_url,
                                                    new_category_url)
137
          membership_list.append(new_category)
138 139
        predicate.edit(membership_criterion_category_list=membership_list,
                       activate_kw=activate_kw)
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154

      # update related recursively if required
      aq_context = aq_base(context)
      if getattr(aq_context, 'listFolderContents', None) is not None:
        for o in context.listFolderContents():
          new_o_category_url = o.getRelativeUrl()
          # Relative Url is based on parent new_category_url so we must
          # replace new_category_url with previous_category_url to find
          # the new category_url for the subobject
          previous_o_category_url = self.updateRelatedCategory(
              new_o_category_url,
              new_category_url,
              previous_category_url)
          self.updateRelatedContent(o, previous_o_category_url,
                                    new_o_category_url)
155

156 157 158 159 160 161
# CMFCategoryTool inherits indirectly from a different CopyContainer which
# lacks our customisations.
# Resolve all inheritence conflicts between CopyContainer (which CategoryTool
# inherits from via BaseTool) and CMFCategoryTool in favour of the property
# from BaseTool (so it may override CopyContainer).
for CopyContainer_property_id in CopyContainer.__dict__:
162
  if CopyContainer_property_id.startswith('__') or CopyContainer_property_id in CategoryTool.__dict__:
163 164 165 166 167
    continue
  try:
    BaseTool_property = getattr(BaseTool, CopyContainer_property_id)
  except AttributeError:
    continue
168 169
  if isinstance(BaseTool_property, ClassSecurityInfo):
    continue
170 171
  setattr(CategoryTool, CopyContainer_property_id, BaseTool_property)

Jean-Paul Smets's avatar
Jean-Paul Smets committed
172
InitializeClass( CategoryTool )