[Checkins] SVN: Products.CMF SyndicationTool now uses an ISydnicationInfo adapter for all object settings.

Charlie Clark charlie at begeistert.org
Tue Oct 5 14:24:36 EDT 2010


Log message for revision 117255:
  SyndicationTool now uses an ISydnicationInfo adapter for all object settings.
  Security added to previously public methods.
  Additional public method added to expose syndication information.
  getHTML4UpdateBase() method deprecated.

Changed:
  U   Products.CMFCore/trunk/Products/CMFCore/interfaces/_tools.py
  U   Products.CMFDefault/trunk/Products/CMFDefault/SyndicationTool.py
  U   Products.CMFDefault/trunk/Products/CMFDefault/tests/test_SyndicationTool.py

-=-
Modified: Products.CMFCore/trunk/Products/CMFCore/interfaces/_tools.py
===================================================================
--- Products.CMFCore/trunk/Products/CMFCore/interfaces/_tools.py	2010-10-05 17:59:37 UTC (rev 117254)
+++ Products.CMFCore/trunk/Products/CMFCore/interfaces/_tools.py	2010-10-05 18:24:36 UTC (rev 117255)
@@ -1289,6 +1289,15 @@
         o falls back to retrieving the site-wide syndication flag
         """
 
+    def getSyndicationInfo(obj):
+        """ Return a dictionary of syndication information for an
+        an object:
+        * period
+        * frequency
+        * base as a DateTime object
+        * max_items
+        """
+
     def getUpdatePeriod(obj=None):
         """ Return the update period for the syndicated feed
 
@@ -1317,6 +1326,7 @@
         """
 
     def getHTML4UpdateBase(obj=None):
+        ### Deprecated
         """ return the HTML-formatted feed publishing base date
 
         o falls back to the site-wide value if no object is passed in

Modified: Products.CMFDefault/trunk/Products/CMFDefault/SyndicationTool.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/SyndicationTool.py	2010-10-05 17:59:37 UTC (rev 117254)
+++ Products.CMFDefault/trunk/Products/CMFDefault/SyndicationTool.py	2010-10-05 18:24:36 UTC (rev 117255)
@@ -10,21 +10,24 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-""" 
-CMFDefault portal_syndication tool.
+""" CMFDefault portal_syndication tool.
 
 Manage outbound RSS syndication of folder content.
 """
 
+from warnings import warn
+
 from AccessControl.SecurityInfo import ClassSecurityInfo
 from Acquisition import aq_base
 from App.class_init import InitializeClass
 from App.special_dtml import HTMLFile
 from DateTime.DateTime import DateTime
 from OFS.SimpleItem import SimpleItem
+
+from zope.component import getAdapter, queryAdapter
 from zope.interface import implements
 
-from Products.CMFCore.interfaces import ISyndicationTool
+from Products.CMFCore.interfaces import ISyndicationTool, ISyndicationInfo
 from Products.CMFCore.PortalFolder import PortalFolderBase
 from Products.CMFCore.utils import _checkPermission
 from Products.CMFCore.utils import registerToolInterface
@@ -37,28 +40,28 @@
 
 
 class SyndicationError(Exception):
-    
+
     pass
-    
 
+
 class SyndicationTool(UniqueObject, SimpleItem):
     """ The syndication tool manages the site-wide policy for
         syndication of folder content as RSS.
     """
     implements(ISyndicationTool)
-    
+
     id = 'portal_syndication'
     meta_type = 'Default Syndication Tool'
-    
+
     security = ClassSecurityInfo()
-    
+
     #Default Sitewide Values
     isAllowed = 0
     syUpdatePeriod = 'daily'
     syUpdateFrequency = 1
     syUpdateBase = DateTime()
     max_items = 15
-    
+
     #ZMI Methods
     manage_options = ( ( { 'label'  : 'Overview'
                          , 'action' : 'overview'
@@ -67,10 +70,10 @@
                          },
                         )
                      )
-    
+
     security.declareProtected(ManagePortal, 'overview')
     overview = HTMLFile('synOverview', _dtmldir)
-    
+
     security.declareProtected(ManagePortal, 'editProperties')
     def editProperties( self
                       , updatePeriod=None
@@ -86,7 +89,7 @@
         """
         if isAllowed is not None:
             self.isAllowed = isAllowed
-        
+
         if updatePeriod is not None:
             self.syUpdatePeriod = updatePeriod
         else:
@@ -94,7 +97,7 @@
                 del self.syUpdatePeriod
             except (AttributeError, KeyError):
                 pass
-        
+
         if updateFrequency is not None:
             self.syUpdateFrequency = int(updateFrequency)
         else:
@@ -102,7 +105,7 @@
                 del self.syUpdateFrequency
             except (AttributeError, KeyError):
                 pass
-        
+
         if updateBase is not None:
             if not hasattr(updateBase, 'ISO'):
                 updateBase = DateTime( updateBase )
@@ -112,7 +115,7 @@
                 del self.syUpdateBase
             except (AttributeError, KeyError):
                 pass
-        
+
         if max_items is not None:
             self.max_items = int(max_items)
         else:
@@ -120,8 +123,17 @@
                 del self.max_items
             except (AttributeError, KeyError):
                 pass
-    
-    security.declarePublic( 'editSyInformationProperties' )
+
+    def _syndication_info(self, obj):
+        """Get a SyndicationInfo adapter for managing object
+        syndication settings
+        """
+        adapter = queryAdapter(obj, ISyndicationInfo)
+        if adapter is None:
+            raise SyndicationError("Syndication is not possible")
+        return adapter
+
+    security.declareProtected(ManageProperties, 'editSyInformationProperties')
     def editSyInformationProperties( self
                                    , obj
                                    , updatePeriod=None
@@ -135,69 +147,42 @@
         These are held on the syndication_information object.
         Not Sitewide Properties.
         """
-        if not _checkPermission( ManageProperties, obj ):
-            raise AccessControl_Unauthorized
-        
-        syInfo = getattr(obj, 'syndication_information', None)
-        
-        if syInfo is None:
+        info = self._syndication_info(obj)
+
+        if not info.enabled:
             raise SyndicationError('Syndication is Disabled')
-        
-        if updatePeriod is not None:
-            syInfo.syUpdatePeriod = updatePeriod
-        else:
-            syInfo.syUpdatePeriod = self.syUpdatePeriod
-        
-        if updateFrequency is not None:
-            syInfo.syUpdateFrequency = int(updateFrequency)
-        else:
-            syInfo.syUpdateFrequency = self.syUpdateFrequency
-        
+
+        updatePeriod = updatePeriod or self.syUpdatePeriod
+        updateFrequency = updateFrequency or self.syUpdateFrequency
+        updateBase = updateBase or self.syUpdateBase
+        max_items = max_items or self.max_items
         if updateBase is not None:
             if not hasattr(updateBase, 'ISO'):
                 updateBase = DateTime( updateBase )
-            syInfo.syUpdateBase = updateBase
-        else:
-            syInfo.syUpdateBase = self.syUpdateBase
-        
-        if max_items is not None:
-            syInfo.max_items = int(max_items)
-        else:
-            syInfo.max_items = self.max_items
-    
-    security.declarePublic('enableSyndication')
+
+        values = {'period': updatePeriod, 'frequency': int(updateFrequency),
+                  'base': updateBase, 'max_items': int(max_items)}
+        info.set_info(**values)
+
+    security.declareProtected(ManageProperties, 'enableSyndication')
     def enableSyndication(self, obj):
         """
         Enable syndication for the obj
         """
         if not self.isSiteSyndicationAllowed():
             raise SyndicationError('Syndication is Disabled')
-        
-        if hasattr(aq_base(obj), 'syndication_information'):
-            raise SyndicationError('Syndication Information Exists')
-        
-        syInfo = SyndicationInformation()
-        obj._setObject('syndication_information', syInfo)
-        syInfo = obj._getOb('syndication_information')
-        syInfo.syUpdatePeriod = self.syUpdatePeriod
-        syInfo.syUpdateFrequency = self.syUpdateFrequency
-        syInfo.syUpdateBase = self.syUpdateBase
-        syInfo.max_items = self.max_items
-        syInfo.description = "Channel Description"
-    
-    security.declarePublic('disableSyndication')
+
+        info = self._syndication_info(obj)
+        info.enable()
+
+    security.declareProtected(ManageProperties, 'disableSyndication')
     def disableSyndication(self, obj):
         """
         Disable syndication for the obj; and remove it.
         """
-        syInfo = getattr(obj, 'syndication_information', None)
-        
-        if syInfo is None:
-            raise SyndicationError('This object does not have Syndication \
-                Information')
-        
-        obj._delObject('syndication_information')
-    
+        info = self._syndication_info(obj)
+        info.disable()
+
     security.declarePublic('getSyndicatableContent')
     def getSyndicatableContent(self, obj):
         """
@@ -209,7 +194,7 @@
         else:
             values = PortalFolderBase.contentValues(obj)
         return values
-    
+
     security.declarePublic('buildUpdatePeriods')
     def buildUpdatePeriods(self):
         """
@@ -222,14 +207,14 @@
                         , ('yearly',  'Yearly')
                         )
         return updatePeriods
-    
+
     security.declarePublic('isSiteSyndicationAllowed')
     def isSiteSyndicationAllowed(self):
         """
         Return sitewide syndication policy
         """
         return self.isAllowed
-    
+
     security.declarePublic('isSyndicationAllowed')
     def isSyndicationAllowed(self, obj=None):
         """
@@ -238,13 +223,19 @@
         particular obj is enabled, allowing for turning on only
         specific folders for syndication.
         """
-        syInfo = getattr(aq_base(obj), 'syndication_information',
-                         None)
-        if syInfo is None:
-            return 0
-        else:
-            return self.isSiteSyndicationAllowed()
-    
+        info = self._syndication_info(obj)
+        return info.enabled
+
+    security.declarePublic('getSyndicationInfo')
+    def getSyndicationInfo(self, obj):
+        """
+        Return a dictionary of syndication parameters
+        """
+        info = self._syndication_info(obj)
+        if not info.enabled:
+            raise SyndicationError('Syndication is not allowed')
+        return info.get_info()
+
     security.declarePublic('getUpdatePeriod')
     def getUpdatePeriod( self, obj=None ):
         """
@@ -252,22 +243,11 @@
         This is either on the object being passed or the
         portal_syndication tool (if a sitewide value or default
         is set)
-        
+
         NOTE:  Need to add checks for sitewide policies!!!
         """
-        if not self.isSiteSyndicationAllowed():
-            raise SyndicationError('Syndication is Not Allowed')
-        
-        if obj is None:
-            return self.syUpdatePeriod
-        
-        syInfo = getattr(obj, 'syndication_information', None)
-        
-        if syInfo is not None:
-            return syInfo.syUpdatePeriod
-        else:
-            return 'Syndication is Not Allowed'
-    
+        return self.getSyndicationInfo(obj)['period']
+
     security.declarePublic('getUpdateFrequency')
     def getUpdateFrequency(self, obj=None):
         """
@@ -275,28 +255,17 @@
         the syn namespace.  This is either on the object being
         pass or the portal_syndication tool (if a sitewide value
         or default is set).
-        
+
         Note:  Need to add checks for sitewide policies!!!
         """
-        if not self.isSiteSyndicationAllowed():
-            raise SyndicationError('Syndication is not Allowed')
-        
-        if obj is None:
-            return self.syUpdateFrequency
-        
-        syInfo = getattr(obj, 'syndication_information',
-                            None)
-        if syInfo is not None:
-            return syInfo.syUpdateFrequency
-        else:
-            return 'Syndication is not Allowed'
-    
+        return self.getSyndicationInfo(obj)['frequency']
+
     security.declarePublic('getUpdateBase')
     def getUpdateBase(self, obj=None):
         """
         Return the base date to be used with the update frequency
         and the update period to calculate a publishing schedule.
-        
+
         Note:  I'm not sure what's best here, creation date, last
         modified date (of the folder being syndicated) or some
         arbitrary date.  For now, I'm going to build a updateBase
@@ -304,57 +273,23 @@
         Additionally, sitewide policy checks might have a place
         here...
         """
-        if not self.isSiteSyndicationAllowed():
-            raise SyndicationError('Syndication is not Allowed')
-        
-        if obj is None:
-            when = self.syUpdateBase
-            return when.ISO()
-        
-        syInfo = getattr(obj, 'syndication_information',
-                            None)
-        if syInfo is not None:
-                when = syInfo.syUpdateBase
-                return when.ISO()
-        else:
-            return 'Syndication is not Allowed'
-    
+        return self.getSyndicationInfo(obj)['base'].ISO()
+
     security.declarePublic('getHTML4UpdateBase')
     def getHTML4UpdateBase(self, obj=None):
         """
         Return HTML4 formated UpdateBase DateTime
         """
-        if not self.isSiteSyndicationAllowed():
-            raise SyndicationError('Syndication is not Allowed')
-        
-        if obj is None:
-            when = self.syUpdateBase
-            return when.HTML4()
-        
-        syInfo = getattr(obj, 'syndication_information',
-                            None)
-        if syInfo is not None:
-            when = syInfo.syUpdateBase
-            return when.HTML4()
-        else:
-            return 'Syndication is not Allowed'
-    
+        warn("RSS 2.0 uses RFC 822 formatting"
+             " this method will be removed in CMF 2.4",
+             DeprecationWarning, stacklevel=2)
+        return self.getSyndicationInfo(obj)['base'].HTML4()
+
     def getMaxItems(self, obj=None):
         """
         Return the max_items to be displayed in the syndication
         """
-        if not self.isSiteSyndicationAllowed():
-            raise SyndicationError('Syndication is not Allowed')
-        
-        if obj is None:
-            return self.max_items
-        
-        syInfo = getattr(obj, 'syndication_information',
-                            None)
-        if syInfo is not None:
-            return syInfo.max_items
-        else:
-            return 'Syndication is not Allowed'
+        return self.getSyndicationInfo(obj)['max_items']
 
 InitializeClass(SyndicationTool)
 registerToolInterface('portal_syndication', ISyndicationTool)

Modified: Products.CMFDefault/trunk/Products/CMFDefault/tests/test_SyndicationTool.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/tests/test_SyndicationTool.py	2010-10-05 17:59:37 UTC (rev 117254)
+++ Products.CMFDefault/trunk/Products/CMFDefault/tests/test_SyndicationTool.py	2010-10-05 18:24:36 UTC (rev 117255)
@@ -17,6 +17,8 @@
 import Testing
 
 from DateTime.DateTime import DateTime
+from zope.component import getSiteManager
+from zope.interface import alsoProvides
 from zope.interface.verify import verifyClass
 from zope.testing.cleanup import cleanUp
 
@@ -29,6 +31,41 @@
         return 'dummy'
 
 
+class DummyInfo:
+
+    def __init__(self, context):
+        self.context = context
+
+    def __call__(self):
+        pass
+
+    @property
+    def enabled(self):
+        if hasattr(self.context, 'enabled'):
+            return self.context.enabled
+        return False
+
+    def enable(self):
+        self.context.enabled = True
+
+    def disable(self):
+        self.context.enabled = False
+
+    def set_info(self, **kw):
+        for k, v in kw.items():
+            setattr(self.context, k, v)
+
+    def get_info(self):
+        if hasattr(self.context, 'frequency'):
+            # values set on context
+            return {'frequency': self.context.frequency,
+                    'period': self.context.period,
+                    'base': self.context.base,
+                    'max_items': self.context.max_items}
+        #values from syndication tool
+        return {'frequency': '1', 'period': 'daily',
+                'base': DateTime('2010/10/04 12:00:00 GMT'), 'max_items': 15}
+
 class SyndicationToolTests(SecurityTest):
 
     def _getTargetClass(self):
@@ -38,6 +75,14 @@
     def _makeOne(self, *args, **kw):
         return self._getTargetClass()(*args, **kw)
 
+    def _makeContext(self):
+        from Products.CMFCore.interfaces import IFolderish, ISyndicationInfo
+        self.folder = folder = Dummy()
+        alsoProvides(folder, IFolderish)
+        sm = getSiteManager()
+        sm.registerAdapter(DummyInfo, [IFolderish], ISyndicationInfo)
+        return folder
+
     def tearDown(self):
         cleanUp()
         SecurityTest.tearDown(self)
@@ -98,20 +143,29 @@
         self.failUnless(tool.isAllowed)
         self.assertEqual(tool.max_items, MAX_ITEMS)
 
-    def test_editSyInformationProperties_disabled(self):
-        from zExceptions import Unauthorized
+    def test_object_not_syndicatable(self):
+        from Products.CMFDefault.SyndicationTool import SyndicationError
+        tool = self._makeOne()
+        self.assertRaises(SyndicationError, tool._syndication_info, Dummy)
 
+    def test_object_is_syndicatable(self):
         tool = self._makeOne()
-        dummy = Dummy()
-        try:
-            tool.editSyInformationProperties(object, updateFrequency=1)
-        except Unauthorized:
-            raise
-        except: # WAAA! it raises a string!
-            pass
-        else:
-            assert 0, "Didn't raise"
+        context = self._makeContext()
+        tool._syndication_info(context)
 
+    def test_object_syndication_is_disabled(self):
+        tool = self._makeOne()
+        context = self._makeContext()
+        info = tool._syndication_info(context)
+        self.assertFalse(tool.isSyndicationAllowed(context))
+
+    def test_enable_object_syndication(self):
+        tool = self._makeOne()
+        tool.isAllowed = True
+        context = self._makeContext()
+        tool.enableSyndication(context)
+        self.assertTrue(tool.isSyndicationAllowed(context))
+
     def test_editSyInformationProperties_normal(self):
         PERIOD = 'hourly'
         FREQUENCY = 4
@@ -119,21 +173,20 @@
         MAX_ITEMS = 42
 
         tool = self._makeOne()
-        dummy = Dummy()
-        info = dummy.syndication_information = Dummy()
+        tool.isAllowed = True
+        context = self._makeContext()
+        tool.enableSyndication(context)
 
-        tool.editSyInformationProperties(dummy,
+        tool.editSyInformationProperties(context,
                                          updatePeriod=PERIOD,
                                          updateFrequency=FREQUENCY,
                                          updateBase=NOW,
                                          max_items=MAX_ITEMS,
                                         )
+        self.assertEqual(tool.getSyndicationInfo(context),
+                             {'frequency': FREQUENCY, 'period': PERIOD,
+                              'base': NOW, 'max_items': MAX_ITEMS})
 
-        self.assertEqual(info.syUpdatePeriod, PERIOD)
-        self.assertEqual(info.syUpdateFrequency, FREQUENCY)
-        self.assertEqual(info.syUpdateBase, NOW)
-        self.assertEqual(info.max_items, MAX_ITEMS)
-
     def test_editSyInformationProperties_coercing(self):
         PERIOD = 'hourly'
         FREQUENCY = 4
@@ -141,21 +194,21 @@
         MAX_ITEMS = 42
 
         tool = self._makeOne()
-        dummy = Dummy()
-        info = dummy.syndication_information = Dummy()
+        tool.isAllowed = True
+        context = self._makeContext()
+        tool.enableSyndication(context)
 
-        tool.editSyInformationProperties(dummy,
+        tool.editSyInformationProperties(context,
                                          updatePeriod=PERIOD,
                                          updateFrequency='%d' % FREQUENCY,
                                          updateBase=NOW.ISO(),
                                          max_items='%d' % MAX_ITEMS,
                                         )
+        self.assertEqual(tool.getSyndicationInfo(context),
+                             {'frequency': FREQUENCY, 'period': PERIOD,
+                              'base': DateTime(NOW.ISO()),
+                              'max_items': MAX_ITEMS})
 
-        self.assertEqual(info.syUpdatePeriod, PERIOD)
-        self.assertEqual(info.syUpdateFrequency, FREQUENCY)
-        self.assertEqual(info.syUpdateBase, DateTime(NOW.ISO()))
-        self.assertEqual(info.max_items, MAX_ITEMS)
-
     def test_editProperties_isAllowedOnly(self):
         # Zope 2.8 crashes if we don't edit all properties.
         # This is because Zope now raises AttributeError



More information about the checkins mailing list