Blame view

product/ERP5/Tool/CategoryTool.py 7.4 KB
Jean-Paul Smets committed
1 2 3
##############################################################################
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
Jean-Paul Smets committed
4
#                    Jean-Paul Smets-Solanes <jp@nexedi.com>
Jean-Paul Smets committed
5 6
#
# WARNING: This program as such is intended to be used by professional
Kazuhiko Shiozaki committed
7
# programmers who take the whole responsibility of assessing all potential
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
Kazuhiko Shiozaki committed
10
# guarantees and support are strongly adviced to contract a Free Software
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 committed
30
ERP5 portal_categories tool.
Jean-Paul Smets committed
31 32
"""

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

from zLOG import LOG

Vincent Pelletier committed
47
class CategoryTool(CMFCategoryTool, BaseTool):
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'
Jean-Paul Smets committed
55
    portal_type     = 'Category Tool'
Jean-Paul Smets committed
56
    allowed_types   = ( 'ERP5 Base Category',)
Julien Muchembled committed
57
    _folder_handler = OFS_HANDLER
Jean-Paul Smets committed
58 59 60 61

    # Declarative Security
    security = ClassSecurityInfo()

Julien Muchembled committed
62 63
    objectValues = BaseTool.objectValues

Jean-Paul Smets committed
64 65 66
    # Filter content (ZMI))
    def filtered_meta_types(self, user=None):
        # Filters the list of available meta types.
Sebastien Robin committed
67
        #all = CMFCategoryTool.inheritedAttribute('filtered_meta_types')(self)
Jean-Paul Smets committed
68 69 70 71 72 73
        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

Sebastien Robin committed
74 75
    # patch, so that we are able to add the BaseCategory
    allowedContentTypes = BaseTool.allowedContentTypes
Vincent Pelletier committed
76
    getVisibleAllowedContentTypeList = BaseTool.getVisibleAllowedContentTypeList
Sebastien Robin committed
77

Yoshinori Okuji committed
78 79 80
    # Override this method to resolve an inheritance problem.
    def _verifyObjectPaste(self, *args, **kw):
      return BaseTool._verifyObjectPaste(self, *args, **kw)
Sebastien Robin committed
81 82 83

    all_meta_types = BaseTool.all_meta_types

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

Kazuhiko Shiozaki committed
88
    security.declareProtected(Permissions.AccessContentsInformation, 'getBaseCategoryDict')
Julien Muchembled committed
89 90 91 92
    @caching_instance_method(
      id='portal_categories.getBaseCategoryDict',
      cache_factory='erp5_content_long',
      cache_id_generator=lambda *a: portal_type_class.last_sync)
Vincent Pelletier committed
93 94 95 96 97 98
    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()
      """
Kazuhiko Shiozaki committed
99
      return dict.fromkeys(self.getBaseCategoryList(), None)
Vincent Pelletier committed
100

Jérome Perrin committed
101 102
    def updateRelatedContent(self, context,
                             previous_category_url, new_category_url):
Kazuhiko Shiozaki committed
103 104 105 106 107
      """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
Jérome Perrin committed
108

Kazuhiko Shiozaki committed
109
      TODO: make this method resist to very large updates (ie. long transaction)
Sebastien Robin committed
110
      """
Jérome Perrin committed
111
      portal_catalog = getToolByName(context, 'portal_catalog')
Kazuhiko Shiozaki committed
112 113 114
      activate_kw = {'tag':'%s_updateRelatedContent' % context.getPath()}

      # udpate category related objects
Jérome Perrin committed
115
      kw = {'category.category_uid': context.getUid(), 'limit': None}
Kazuhiko Shiozaki committed
116 117 118 119 120 121 122 123 124 125 126 127
      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
Jérome Perrin committed
128
      kw = {'predicate_category.category_uid': context.getUid(), 'limit': None}
Kazuhiko Shiozaki committed
129 130
      for predicate in portal_catalog(**kw):
        predicate = predicate.getObject()
Sebastien Robin committed
131 132
        membership_list = []
        for category in predicate.getMembershipCriterionCategoryList():
Jérome Perrin committed
133 134 135
          new_category = self.updateRelatedCategory(category,
                                                    previous_category_url,
                                                    new_category_url)
Sebastien Robin committed
136
          membership_list.append(new_category)
Kazuhiko Shiozaki committed
137 138
        predicate.edit(membership_criterion_category_list=membership_list,
                       activate_kw=activate_kw)
Kazuhiko Shiozaki committed
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153

      # 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)
Sebastien Robin committed
154

Vincent Pelletier committed
155 156 157 158 159 160
# 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__:
Vincent Pelletier committed
161
  if CopyContainer_property_id.startswith('__') or CopyContainer_property_id in CategoryTool.__dict__:
Vincent Pelletier committed
162 163 164 165 166
    continue
  try:
    BaseTool_property = getattr(BaseTool, CopyContainer_property_id)
  except AttributeError:
    continue
Vincent Pelletier committed
167 168
  if isinstance(BaseTool_property, ClassSecurityInfo):
    continue
Vincent Pelletier committed
169 170
  setattr(CategoryTool, CopyContainer_property_id, BaseTool_property)

Jean-Paul Smets committed
171
InitializeClass( CategoryTool )