[Zope3-checkins] CVS: Products3/NewsSite - index_rss.pt:1.1 browser.py:1.2 configure.zcml:1.10 interfaces.py:1.4 newssite.py:1.4
Tres Seaver
tseaver@zope.com
Wed, 26 Mar 2003 12:18:17 -0500
Update of /cvs-repository/Products3/NewsSite
In directory cvs.zope.org:/tmp/cvs-serv9365
Modified Files:
browser.py configure.zcml interfaces.py newssite.py
Added Files:
index_rss.pt
Log Message:
- browser.py:
o Add new view class for editing the syndication policies through
an adapter.
o Add another view class for generating RSS (note that this page
should probably be folded into the "main" view, to allow reusing
its filtering mechanisms).
- interfaces.py:
o Add schema for syndication policies.
- newssite.py:
o Normalize some imports.
o Create adapter for ISyndicationPolicies, which stores the schema
values in an annontation.
- configure.zcml:
o Annotate the configuration, grouping related bits together.
o Add configuration for syndication views.
=== Added File Products3/NewsSite/index_rss.pt ===
<?xml version="1.0"?>
<rdf:RDF
xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:syn="http://purl.org/rss/1.0/modules/syndication/"
xmlns="http://purl.org/rss/1.0/"
tal:define="info_items view/listSyndicationInfo" >
<channel rdf:about="SITE_URL"
tal:attributes="rdf:about string:${view/context_url}/index.rss;" >
<title tal:content="view/dc/title">SITE TITLE</title>
<link tal:content="view/context_url" />
<description tal:content="view/dc/description">
SITE DESCRIPTION
</description>
<dc:rights tal:content="view/dc/rights | default">RIGHTS</dc:rights>
<dc:language tal:content="view/dc/language | default">en/US</dc:language>
<dc:publisher
tal:content="view/dc/publisher | default">PUBLISHER</dc:publisher>
<syn:updatePeriod tal:content="view/syn_policies/period"
>SYNDICATION UPDATE PERIOD</syn:updatePeriod>
<syn:updateFrequency tal:content="view/syn_policies/frequency"
>SYNDICATION UPDATE FREQUENCY</syn:updateFrequency>
<syn:updateBase tal:content="view/syn_policies/base_update"
>SYNDICATION BASE DATE</syn:updateBase>
<items>
<rdf:Seq>
<rdf:li rdf:resource="ENTRY_URL"
tal:repeat="item info_items"
tal:attributes="rdf:resource item/entry_url"
/>
</rdf:Seq>
</items>
</channel>
<tal:loop tal:repeat="item info_items"
><item rdf:about="ENTRY_URL"
tal:attributes="rdf:about item/entry_url"
>
<title tal:content="item/dc/title">ENTRY TITLE</title>
<description tal:content="item/dc/description">ENTRY DESC.</description>
<link tal:content="item/entry_url" />
<dc:creator tal:content="item/dc/Creator" />
<dc:date tal:content="item/dc/Date" />
</item>
</tal:loop>
</rdf:RDF>
=== Products3/NewsSite/browser.py 1.1 => 1.2 ===
--- Products3/NewsSite/browser.py:1.1 Wed Mar 26 07:14:49 2003
+++ Products3/NewsSite/browser.py Wed Mar 26 12:18:16 2003
@@ -16,9 +16,78 @@
$Id$
"""
+from zope.component import getAdapter
+from zope.component import getView
+from zope.component import queryUtility
+
+from zope.proxy.context import ContextWrapper
+
+from zope.app.interfaces.dublincore import IZopeDublinCore
from zope.app.browser.container.adding import Adding
+from interfaces import INewsSite
+from interfaces import ISyndicationPolicies
+
class NewsSiteAdding(Adding):
"""Custom adding view for NewsSite objects.
"""
menu_id = "add_news"
+
+class NewsSiteSyndicationPoliciesView:
+
+ """ Simple class for managing the RSS policies for the site.
+ """
+ __used_for__ = INewsSite
+
+class NewsSiteSyndicationView:
+
+ """ Provide an interface for syndicating a NewsSite over RSS.
+ """
+ __used_for__ = INewsSite
+
+ def __init__(self, context, request):
+
+ self.context = context
+ self.request = request
+ self.site = getAdapter(context, INewsSite)
+ self.dc = getAdapter(context, IZopeDublinCore)
+ self.context_url = getView(context, 'absolute_url', request)()
+
+ # XXX
+ self.logo_url = 'newssite_logo.png'
+
+ self.syn_policies = getAdapter(context, ISyndicationPolicies)
+
+ def listSortedEntries(self):
+
+ result = []
+ for k, v in self.site.items():
+ dc = getAdapter(v, IZopeDublinCore)
+ result.append((dc.effective or dc.created, k))
+ return [x[1] for x in result]
+
+
+ def listSyndicationInfo(self):
+
+ """ Return a sequence of mappings describing the syndicatable entries.
+ """
+ result = []
+
+ keys = self.listSortedEntries()
+
+ for key in keys[:self.syn_policies.max_items]:
+
+ raw_entry = self.site[key]
+ wrapped_entry = ContextWrapper(raw_entry, self.context,
+ name=key)
+
+ entry_url = getView(wrapped_entry, 'absolute_url', self.request)
+ entry_dc = getAdapter(wrapped_entry, IZopeDublinCore)
+
+ result.append( { 'title' : entry_dc.title
+ , 'entry' : wrapped_entry
+ , 'entry_url' : entry_url()
+ , 'dc' : entry_dc
+ } )
+
+ return result
=== Products3/NewsSite/configure.zcml 1.9 => 1.10 ===
--- Products3/NewsSite/configure.zcml:1.9 Wed Mar 26 10:14:14 2003
+++ Products3/NewsSite/configure.zcml Wed Mar 26 12:18:16 2003
@@ -3,58 +3,68 @@
xmlns:browser='http://namespaces.zope.org/browser'
>
-<browser:menu id="add_news" title="News Site Items" />
+<!-- Declare a special menu for the content to be added within
+ :: a NewsSite. The custom "adding" view for the site uses this
+ :: menu, and the content objects will insert their add forms into
+ :: it.
+ -->
+<browser:menu
+ id="add_news"
+ title="News Site Items"
+ />
<include package=".NewsItem" />
-<!-- Configuration for News Site Object -->
+
+<!-- Configuration for News Site Object
+ -->
<content
- class=".newssite.NewsSite">
+ class=".newssite.NewsSite">
+
+ <implements
+ interface="zope.app.interfaces.annotation.IAttributeAnnotatable"
+ />
+
+ <factory
+ id="NewsSite"
+ title="News site"
+ description="boring news site"
+ permission="zope.ManageContent"
+ />
+
+ <allow
+ interface="zope.app.interfaces.services.service.Read"
+ />
+
+ <require
+ permission="zope.ManageServices"
+ interface="zope.app.interfaces.services.service.Write"
+ />
+
+ <require
+ permission="zope.View"
+ interface="zope.app.interfaces.container.IReadContainer"
+ />
+
+ <require
+ permission="zope.ManageContent"
+ interface="zope.app.interfaces.container.IWriteContainer"
+ />
- <factory
- id="NewsSite"
- permission="zope.ManageContent"
- title="News site"
- description="boring news site"/>
-
- <allow
- interface="zope.app.interfaces.services.service.Read"
- />
-
- <require
- permission="zope.ManageServices"
- interface="zope.app.interfaces.services.service.Write"
- />
-
- <require
- permission="zope.View"
- interface="zope.app.interfaces.container.IReadContainer"
- />
-
- <require
- permission="zope.ManageContent"
- interface="zope.app.interfaces.container.IWriteContainer"
- />
-
</content>
+<!-- Register us with the "global" add list.
+ -->
<browser:menuItem
- menu="add_content"
- for="zope.app.interfaces.container.IAdding"
- title="News site"
- action="NewsSite"
+ menu="add_content"
+ for="zope.app.interfaces.container.IAdding"
+ title="News site"
+ action="NewsSite"
description="A boring news site."
permission="zope.View"
-/>
-
-<browser:page
- template="newsindex.pt"
- name="list.html"
- for=".interfaces.INewsSite"
- permission="zope.View"
- menu="zmi_views"
- title="View news index"
- class=".newssite.NewsSiteView"/>
+ />
+<!-- Custom adding view.
+ -->
<browser:view
for=".interfaces.INewsSite"
name="+"
@@ -63,10 +73,63 @@
permission="zope.ManageContent"
allowed_attributes="addingInfo"
>
-
<browser:page name="index.html" template="add.pt" />
<browser:page name="action.html" attribute="action" />
</browser:view>
+
+<!-- Default view.
+ ::
+ :: XXX: This should be named 'index.html', obviating the need for the
+ :: 'browser:defaultView' below; however, some other directive is
+ :: stomping on our registration if we do that! :(
+ -->
+<browser:page
+ template="newsindex.pt"
+ name="list.html"
+ for=".interfaces.INewsSite"
+ permission="zope.View"
+ menu="zmi_views"
+ title="View news index"
+ class=".newssite.NewsSiteView"
+ />
+
+<browser:defaultView
+ name="list.html"
+ />
+
+<!-- Render RSS for our items.
+ -->
+<browser:page
+ for=".interfaces.INewsSite"
+ name="index.rss"
+ template="index_rss.pt"
+ menu="zmi_views"
+ title="RSS"
+ class=".browser.NewsSiteSyndicationView"
+ permission="zope.View"
+ />
+
+<!-- Adapt the NewsSite to ISyndicationPolicies.
+ -->
+<adapter
+ factory=".newssite.NewsSiteSyndicationPoliciesAdapter"
+ provides=".interfaces.ISyndicationPolicies"
+ for=".interfaces.INewsSite" />
+
+<!-- Use the adapter to edit the syndication policies.
+ -->
+<browser:editform
+ name="syndication.html"
+ schema=".interfaces.ISyndicationPolicies"
+ label="Syndication Policies"
+ permission="zope.ManageContent"
+ for=".interfaces.INewsSite"
+ class=".browser.NewsSiteSyndicationPoliciesView"
+ menu="zmi_views"
+ title="Syndication"
+ />
+
+
<!--
<browser:addform
name="register.html"
=== Products3/NewsSite/interfaces.py 1.3 => 1.4 ===
--- Products3/NewsSite/interfaces.py:1.3 Wed Mar 26 06:54:09 2003
+++ Products3/NewsSite/interfaces.py Wed Mar 26 12:18:16 2003
@@ -17,12 +17,21 @@
"""
from zope.interface import Interface
-from zope.schema import TextLine, Password
+from zope.proxy.context import ContextProperty
+from zope.schema import TextLine, Password, Int, Datetime
+
from zope.app.interfaces.container import IContentContainer
+from zope.app.datetimeutils import parseDatetimetz
+#
+# Content interfacse
+#
class INewsSite(IContentContainer):
"""Provides a marker interface for news site"""
+#
+# Registration interfaces
+#
class IMember(Interface):
"""Provide information about site members.
@@ -42,6 +51,46 @@
required=True)
+
+#
+# Syndication policy interfaces
+#
+ALLOWED_PERIODS = (u'hourly', u'daily', u'weekly', u'monthly', u'yearly')
+
+class SyndicationPeriodField(TextLine):
+
+ def _allowed(self):
+ return ALLOWED_PERIODS
+
+ allowed_values = ContextProperty( _allowed )
+
+class ISyndicationPolicies(Interface):
+ """ Track a set of policy settings for syndicating our news over RSS.
+ """
+ max_items = Int(title=u'Max. RSS Items',
+ description=u'The number of entries available to RSS.',
+ required=True,
+ default=15
+ )
+
+ period = SyndicationPeriodField(
+ title=u'Update Period',
+ description=u'Periodicity of update to the corpus.',
+ required=True,
+ default=u'daily'
+ )
+
+ frequency = Int(title=u'Frequency',
+ description=u'How often per period is the corpus updated?',
+ required=True,
+ default=1
+ )
+
+ base_update = Datetime(title=u'Base Update Time',
+ description=u'Starting point for computing updates.',
+ required=True,
+ default=parseDatetimetz( '2003/01/01 00:00 UTC' )
+ )
=== Products3/NewsSite/newssite.py 1.3 => 1.4 ===
--- Products3/NewsSite/newssite.py:1.3 Wed Mar 26 11:53:39 2003
+++ Products3/NewsSite/newssite.py Wed Mar 26 12:18:16 2003
@@ -15,12 +15,18 @@
$Id$
"""
-from zope.app.content.folder import Folder
-from zopeproducts.NewsSite.interfaces import INewsSite
-from zopeproducts.NewsSite.NewsItem.interfaces import INewsItem
+from zope.app.datetimeutils import parseDatetimetz
+from persistence.dict import PersistentDict
+
from zope.publisher.browser import BrowserView
-from zope.app.interfaces.dublincore import ICMFDublinCore
from zope.component import getAdapter
+from zope.app.interfaces.annotation import IAnnotations
+from zope.app.interfaces.dublincore import ICMFDublinCore
+from zope.app.content.folder import Folder
+
+from interfaces import INewsSite
+from interfaces import ISyndicationPolicies
+from NewsItem.interfaces import INewsItem
from zope.proxy.context import ContextWrapper
class NewsSite(Folder):
@@ -28,7 +34,7 @@
__implements__ = (Folder.__implements__, INewsSite)
-
+
class NewsSiteView(BrowserView):
__used_for__ = INewsSite
@@ -46,3 +52,93 @@
list = [ x[0] for x in list]
return list
+#
+# Adapter for storing ISyndicationPolicies as annotations.
+#
+SPkey = "zopeproducts.NewsSite.SyndicationPolicies"
+
+class NewsSiteSyndicationPoliciesAdapter( object ):
+
+ """ Adapt news site to ISP interface.
+ """
+
+ __implements__ = ( ISyndicationPolicies, )
+ __used_for__ = ( INewsSite, )
+
+ _annotations = None
+
+ _DEFAULT_MAX_ITEMS = 15
+ _DEFAULT_PERIOD = 'daily'
+ _DEFAULT_FREQUENCY = 1
+ _DEFAULT_BASE_UPDATE = parseDatetimetz( '2003/01/01' )
+
+ def __init__( self, context ):
+
+ annotations = getAdapter( context, IAnnotations )
+
+ spdata = annotations.get( SPkey )
+
+ if not spdata:
+ self._annotations = annotations
+ spdata = PersistentDict()
+ spdata[ 'max_items' ] = self._DEFAULT_MAX_ITEMS
+ spdata[ 'period' ] = self._DEFAULT_PERIOD
+ spdata[ 'frequency' ] = self._DEFAULT_FREQUENCY
+ spdata[ 'base_update' ] = self._DEFAULT_BASE_UPDATE
+
+ self._mapping = spdata
+
+ def _changed( self ):
+
+ # Is this a new annotation?
+ if self._annotations is not None:
+ self._annotations[ SPkey ] = self._mapping
+ self._annotations = None
+
+ #
+ # 'max_items' field
+ #
+ def _get_max_items( self ):
+ return self._mapping[ 'max_items' ]
+
+ def _set_max_items( self, value ):
+ self._mapping[ 'max_items' ] = value
+ self._changed()
+
+ max_items = property( _get_max_items, _set_max_items )
+
+ #
+ # 'period' field
+ #
+ def _get_period( self ):
+ return self._mapping[ 'period' ]
+
+ def _set_period( self, value ):
+ self._mapping[ 'period' ] = value
+ self._changed()
+
+ period = property( _get_period, _set_period )
+
+ #
+ # 'freqeuency' field
+ #
+ def _get_frequency( self ):
+ return self._mapping[ 'frequency' ]
+
+ def _set_frequency( self, value ):
+ self._mapping[ 'frequency' ] = value
+ self._changed()
+
+ frequency = property( _get_frequency, _set_frequency )
+
+ #
+ # 'base_update' field
+ #
+ def _get_base_update( self ):
+ return self._mapping[ 'base_update' ]
+
+ def _set_base_update( self, value ):
+ self._mapping[ 'base_update' ] = value
+ self._changed()
+
+ base_update = property( _get_base_update, _set_base_update )