Back to index

plone3  3.1.7
AllowedTypesByIface.py
Go to the documentation of this file.
00001 ###############################################################################
00002 #
00003 # Copyright (c) 2002-2005, Benjamin Saller <bcsaller@ideasuite.com>, and
00004 #                              the respective authors. All rights reserved.
00005 # For a list of Archetypes contributors see docs/CREDITS.txt.
00006 #
00007 # Redistribution and use in source and binary forms, with or without
00008 # modification, are permitted provided that the following conditions are met:
00009 #
00010 # * Redistributions of source code must retain the above copyright notice, this
00011 #   list of conditions and the following disclaimer.
00012 # * Redistributions in binary form must reproduce the above copyright notice,
00013 #   this list of conditions and the following disclaimer in the documentation
00014 #   and/or other materials provided with the distribution.
00015 # * Neither the name of the author nor the names of its contributors may be
00016 #   used to endorse or promote products derived from this software without
00017 #   specific prior written permission.
00018 #
00019 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
00020 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
00022 # FOR A PARTICULAR PURPOSE.
00023 #
00024 ###############################################################################
00025 
00026 from Products.CMFCore.utils import getToolByName
00027 from Products.Archetypes.atapi import BaseFolder
00028 from Products.Archetypes.ArchetypeTool import listTypes
00029 
00030 class AllowedTypesByIfaceMixin:
00031     """An approach to restrict allowed content types in a container by
00032     the interfaces they implement.
00033 
00034     Notice that extending this class means surpassing allowed_content_types,
00035     filter_content_types etc in the FTI, while we are still concerned about
00036     security.
00037 
00038     ATBIFolder is an example type that uses AllowedTypesByIfaceMixin:
00039 
00040     >>> self.folder.invokeFactory('ATBIFolder', 'f')
00041     'f'
00042 
00043     >>> f = self.folder.f
00044 
00045     f has an empty list of allowed_interfaces, so it doesn't allow anything
00046     right now:
00047 
00048     >>> f.allowedContentTypes()
00049     []
00050 
00051     invokeFactory will fail:
00052 
00053     >>> try:
00054     ...     f.invokeFactory('SimpleType', 'st')
00055     ... except ValueError:
00056     ...     print 'Right'
00057     Right
00058 
00059     Now we restrict allowed_interfaces to IBaseFolder:
00060 
00061     >>> from Products.Archetypes.interfaces.base import *
00062     >>> f.allowed_interfaces = (IBaseFolder,)
00063 
00064     And try to add a SimpleType, which fails again:
00065 
00066     >>> try:
00067     ...     f.invokeFactory('SimpleType', 'st')
00068     ... except ValueError:
00069     ...     print 'Right'
00070     Right
00071 
00072     SimpleFolder implements IBaseFolder:
00073 
00074     >>> f.invokeFactory('SimpleFolder', 'sf')
00075     'sf'
00076 
00077     A content object only needs to implement one of allowed_interfaces:
00078 
00079     >>> from Interface import Interface
00080     >>> class SomeInterface(Interface): pass
00081     >>> f.allowed_interfaces = (IBaseFolder, SomeInterface)
00082     >>> f.invokeFactory('SimpleFolder', 'sf2')
00083     'sf2'
00084     >>> try:
00085     ...     f.invokeFactory('SimpleType', 'sf')
00086     ... except ValueError:
00087     ...     print 'Right'
00088     Right
00089     
00090     """
00091 
00092     # XXX: This class depends heavily on implementation details in CMF's
00093     #      PortalFolder.
00094 
00095     allowed_interfaces = () # Don't allow anything, subclasses overwrite!
00096 
00097     def allowedContentTypes(self):
00098         """Redefines CMF PortalFolder's allowedContentTypes."""
00099         at = getToolByName(self, 'archetype_tool')
00100         return at.listPortalTypesWithInterfaces(self.allowed_interfaces)
00101 
00102     def invokeFactory(self, type_name, id, RESPONSE = None, *args, **kwargs):
00103         """Invokes the portal_types tool.
00104 
00105         Overrides PortalFolder.invokeFactory."""
00106         pt = getToolByName(self, 'portal_types')
00107         at = getToolByName(self, 'archetype_tool')
00108         fti = None
00109         for t in listTypes():
00110             if t['portal_type'] == type_name:
00111                 fti = t
00112                 break
00113         
00114         if fti is None:
00115             raise ValueError, "Type %r not available." % type_name
00116 
00117         if not at.typeImplementsInterfaces(fti, self.allowed_interfaces):
00118             raise ValueError, "Type %r does not implement any of %s." % \
00119                   (type_name, self.allowed_interfaces)
00120 
00121         args = (type_name, self, id, RESPONSE) + args
00122         new_id = pt.constructContent(*args, **kwargs)
00123         if not new_id: new_id = id
00124         return new_id
00125 
00126     def _verifyObjectPaste(self, object, validate_src=1):
00127         """Overrides PortalFolder._verifyObjectPaste."""
00128         # XXX: What we do here is trick
00129         #      PortalFolder._verifyObjectPaste in its check for
00130         #      allowed content types. We make our typeinfo temporarily
00131         #      unavailable.
00132         pt = getToolByName(self, 'portal_types')
00133         tmp_name = '%s_TMP' % self.portal_type
00134         ti = pt.getTypeInfo(self.portal_type)
00135         pt.manage_delObjects([self.portal_type])
00136         try:
00137             value = BaseFolder._verifyObjectPaste(self, object, validate_src)
00138         finally:
00139             pt._setObject(self.portal_type, ti)
00140         return value