[z3-checkins] r9222 - in z3/zblog: . branch tag trunk trunk/browser trunk/browser/ftests trunk/tests

rspivak at codespeak.net rspivak at codespeak.net
Mon Feb 14 22:04:33 MET 2005


Author: rspivak
Date: Mon Feb 14 22:04:32 2005
New Revision: 9222

Added:
   z3/zblog/
   z3/zblog/branch/
   z3/zblog/tag/
   z3/zblog/trunk/
   z3/zblog/trunk/Makefile
   z3/zblog/trunk/__init__.py
   z3/zblog/trunk/blog.py
   z3/zblog/trunk/blogentry.py
   z3/zblog/trunk/browser/
   z3/zblog/trunk/browser/__init__.py
   z3/zblog/trunk/browser/blog.png   (contents, props changed)
   z3/zblog/trunk/browser/blog.py
   z3/zblog/trunk/browser/blogentry.png   (contents, props changed)
   z3/zblog/trunk/browser/blogentry.py
   z3/zblog/trunk/browser/blogentrydetails.pt
   z3/zblog/trunk/browser/bloglisting.pt
   z3/zblog/trunk/browser/configure.zcml
   z3/zblog/trunk/browser/dublincore.py
   z3/zblog/trunk/browser/ftests/
   z3/zblog/trunk/browser/ftests/__init__.py
   z3/zblog/trunk/browser/ftests/test_blogentry.py
   z3/zblog/trunk/configure.zcml
   z3/zblog/trunk/interfaces.py
   z3/zblog/trunk/tests/
   z3/zblog/trunk/tests/__init__.py
   z3/zblog/trunk/tests/test_blog.py
   z3/zblog/trunk/tests/test_blogentry.py
Log:
initial import

Added: z3/zblog/trunk/Makefile
==============================================================================
--- (empty file)
+++ z3/zblog/trunk/Makefile	Mon Feb 14 22:04:32 2005
@@ -0,0 +1,5 @@
+.PHONY: clean
+
+clean:
+	find . "(" -name "*~" -or  -name ".#*" -or -name "*.pyc" ")" -print | xargs rm -f
+

Added: z3/zblog/trunk/__init__.py
==============================================================================
--- (empty file)
+++ z3/zblog/trunk/__init__.py	Mon Feb 14 22:04:32 2005
@@ -0,0 +1 @@
+#
\ No newline at end of file

Added: z3/zblog/trunk/blog.py
==============================================================================
--- (empty file)
+++ z3/zblog/trunk/blog.py	Mon Feb 14 22:04:32 2005
@@ -0,0 +1,62 @@
+# $Id$
+
+from zope.interface import implements
+from zope.component import adapts
+from zope.app.container.btree import BTreeContainer
+from zope.app.container.interfaces import INameChooser
+
+from zblog.interfaces import IBlog
+import re
+from datetime import datetime
+
+class Blog(BTreeContainer):
+    """Blog implementation using BTree container.
+
+    Make sure that Blog implements IBlog interface:
+
+    >>> from zope.interface.verify import verifyClass
+    >>> verifyClass(IBlog, Blog)
+    True
+
+    Example of changing blog title and description:
+
+    >>> blog = Blog()
+
+    >>> blog.title
+    u''
+    >>> blog.title = u'Blog title'
+    >>> blog.title
+    u'Blog title'
+
+    >>> blog.description
+    u''
+    >>> blog.description = u'Blog description'
+    >>> blog.description
+    u'Blog description'
+    """
+
+    implements(IBlog)
+
+    # See zblog.interfaces.IBlog
+    description = u''
+
+    # See zblog.interfaces.IBlog
+    title = u''
+
+
+class BlogNameChooser:
+    """An adapter to choose names for blog entries."""
+    implements(INameChooser)
+    adapts(IBlog)
+
+    def __init__(self, context):
+        self.context = context
+
+    def chooseName(self, name, entry):
+        if name:
+            return name
+        return datetime.now().strftime('%Y_%m_%d_') + \
+               re.sub('\s', '_', entry.title)
+
+    def checkName(self, name, entry):
+        return True

Added: z3/zblog/trunk/blogentry.py
==============================================================================
--- (empty file)
+++ z3/zblog/trunk/blogentry.py	Mon Feb 14 22:04:32 2005
@@ -0,0 +1,50 @@
+# $Id$
+
+from zope.interface import implements
+from persistent import Persistent
+from zope.app.container.contained import Contained
+from zblog.interfaces import IBlogEntry, IBlogEntryContained
+
+class BlogEntry(Persistent, Contained):
+    """Blog entry.
+
+    Make sure that BlogEntry implements IBlogEntry interface:
+
+    >>> from zope.interface.verify import verifyClass
+    >>> verifyClass(IBlogEntry, BlogEntry)
+    True
+
+    Testing title, content:
+
+    >>> blogentry = BlogEntry()
+
+    >>> blogentry.title
+    u''
+    >>> blogentry.title = u'Blog entry title'
+    >>> blogentry.title
+    u'Blog entry title'
+
+    >>> blogentry.content
+    u''
+    >>> blogentry.content = u'Blog entry content'
+    >>> blogentry.content
+    u'Blog entry content'
+
+    Example of changing summary field:
+
+    >>> blogentry.summary
+    u''
+    >>> blogentry.summary = u'Blog entry summary'
+    >>> blogentry.summary
+    u'Blog entry summary'
+    """
+    implements(IBlogEntry, IBlogEntryContained)
+
+    # See zblog.interfaces.IBlogEntry
+    title = u''
+
+    # See zblog.interfaces.IBlogEntry
+    content = u''
+
+    # See zblog.interfaces.IBlogEntry
+    summary = u''

Added: z3/zblog/trunk/browser/__init__.py
==============================================================================
--- (empty file)
+++ z3/zblog/trunk/browser/__init__.py	Mon Feb 14 22:04:32 2005
@@ -0,0 +1 @@
+#

Added: z3/zblog/trunk/browser/blog.png
==============================================================================
Binary file. No diff available.

Added: z3/zblog/trunk/browser/blog.py
==============================================================================
--- (empty file)
+++ z3/zblog/trunk/browser/blog.py	Mon Feb 14 22:04:32 2005
@@ -0,0 +1,14 @@
+# $Id$
+
+from zope.app import zapi
+from zope.app.dublincore.interfaces import ICMFDublinCore
+
+class BlogListing:
+
+    def getSortedBlogEntries(self):
+        """Returns blog entries sorted on creation date in reverse order."""
+        items = [(ICMFDublinCore(entry).created, entry) for
+                 entry in self.context.values()]
+        items.sort()
+        items.reverse()
+        return [t[1] for t in items]

Added: z3/zblog/trunk/browser/blogentry.png
==============================================================================
Binary file. No diff available.

Added: z3/zblog/trunk/browser/blogentry.py
==============================================================================
--- (empty file)
+++ z3/zblog/trunk/browser/blogentry.py	Mon Feb 14 22:04:32 2005
@@ -0,0 +1,4 @@
+from zblog.browser.dublincore import DublinCoreViews
+
+class BlogEntryDetails(DublinCoreViews):
+    pass

Added: z3/zblog/trunk/browser/blogentrydetails.pt
==============================================================================
--- (empty file)
+++ z3/zblog/trunk/browser/blogentrydetails.pt	Mon Feb 14 22:04:32 2005
@@ -0,0 +1,12 @@
+<html metal:use-macro="views/standard_macros/view">
+  <body>
+    <div metal:fill-slot="body">
+      <h1 tal:content="context/title">Blog entry title</h1>
+      <div class="row" tal:content="context/content" />
+      <div class="row">
+	<span tal:content="view/author" />
+	<span tal:content="view/created" />
+      </div>
+    </div>
+  </body>
+</html>

Added: z3/zblog/trunk/browser/bloglisting.pt
==============================================================================
--- (empty file)
+++ z3/zblog/trunk/browser/bloglisting.pt	Mon Feb 14 22:04:32 2005
@@ -0,0 +1,14 @@
+<html metal:use-macro="views/standard_macros/view">
+  <body>
+    <div metal:fill-slot="body">
+      <h1 tal:content="context/title"></h1>
+      <div tal:content="context/description" />
+
+      <div class="row" tal:repeat="item view/getSortedBlogEntries">
+	<div class="row" tal:content="item/title" />
+	<div class="row" tal:content="item/summary" />
+	<hr/>
+      </div>
+    </div>
+  </body>
+</html>

Added: z3/zblog/trunk/browser/configure.zcml
==============================================================================
--- (empty file)
+++ z3/zblog/trunk/browser/configure.zcml	Mon Feb 14 22:04:32 2005
@@ -0,0 +1,117 @@
+<configure
+    xmlns="http://namespaces.zope.org/browser">
+
+  <!-- Blog -->
+
+  <addform
+      label="Add Blog"
+      name="AddBlog.html"
+      schema="zblog.interfaces.IBlog"
+      content_factory="zblog.blog.Blog"
+      fields="title description"
+      permission="zope.ManageContent"
+      />
+
+  <addMenuItem
+      class="zblog.blog.Blog"
+      title="Blog"
+      description="A Blog"
+      permission="zope.ManageContent"
+      view="AddBlog.html"
+      />
+
+  <editform
+      schema="zblog.interfaces.IBlog"
+      for="zblog.interfaces.IBlog"
+      label="Change Blog"
+      name="edit.html"
+      permission="blog.Edit"
+      menu="zmi_views" title="Edit"
+      />
+
+  <containerViews
+      for="zblog.interfaces.IBlog"
+      index="blog.View"
+      contents="blog.Edit"
+      add="blog.Add"
+      />
+
+  <page
+      name="view.html"
+      for="zblog.interfaces.IBlog"
+      class=".blog.BlogListing"
+      template="bloglisting.pt"
+      permission="blog.View"
+      menu="zmi_views" title="Preview" />
+
+  <defaultView
+      for="zblog.interfaces.IBlog"
+      name="view.html" />
+
+  <icon
+      name="zmi_icon"
+      for="zblog.interfaces.IBlog"
+      file="blog.png" />
+
+
+  <!-- Blog Entry -->
+
+  <addform
+      label="Add Blog Entry"
+      name="AddBlogEntry.html"
+      schema="zblog.interfaces.IBlogEntry"
+      content_factory="zblog.blogentry.BlogEntry"
+      fields="title content summary"
+      permission="blog.Add"
+      set_before_add="title"
+      />
+
+  <addMenuItem
+      class="zblog.blogentry.BlogEntry"
+      title="Blog Entry"
+      description="A Blog Entry"
+      permission="blog.Add"
+      view="AddBlogEntry.html"
+      />
+
+  <editform
+      schema="zblog.interfaces.IBlogEntry"
+      for="zblog.interfaces.IBlogEntry"
+      label="Change Blog Entry"
+      name="edit.html"
+      permission="blog.Edit"
+      menu="zmi_views" title="Edit"
+      />
+
+  <page
+      name="details.html"
+      for="zblog.interfaces.IBlogEntry"
+      class=".blogentry.BlogEntryDetails"
+      template="blogentrydetails.pt"
+      permission="blog.View"
+      menu="zmi_views" title="Preview"
+      />
+
+  <defaultView
+      for="zblog.interfaces.IBlogEntry"
+      name="details.html" />
+
+  <icon
+      name="zmi_icon"
+      for="zblog.interfaces.IBlogEntry"
+      file="blogentry.png" />
+
+  <!-- separate menu for Blog|Entry and Blog -->
+  <menu
+      id="blogentry_actions"
+      title="Menu for BlogEntry related actions"
+      />
+
+  <menuItems
+      menu="blogentry_actions"
+      for="zblog.interfaces.IBlogEntry">
+    <menuItem title="View" action="@@details.html" />
+    <menuItem title="Edit" action="@@edit.html" />
+  </menuItems>
+
+</configure>

Added: z3/zblog/trunk/browser/dublincore.py
==============================================================================
--- (empty file)
+++ z3/zblog/trunk/browser/dublincore.py	Mon Feb 14 22:04:32 2005
@@ -0,0 +1,16 @@
+from zope.app.dublincore.interfaces import ICMFDublinCore
+
+class DublinCoreViews:
+
+    def author(self):
+        creators = ICMFDublinCore(self.context).creators
+        if creators is None:
+            return u'unknown'
+        return creators[0]
+
+    def created(self):
+        date = ICMFDublinCore(self.context).created
+        if date is None:
+            return ''
+        formatter = self.request.locale.dates.getFormatter('dateTime', 'medium')
+        return formatter.format(date)

Added: z3/zblog/trunk/browser/ftests/__init__.py
==============================================================================
--- (empty file)
+++ z3/zblog/trunk/browser/ftests/__init__.py	Mon Feb 14 22:04:32 2005
@@ -0,0 +1 @@
+#

Added: z3/zblog/trunk/browser/ftests/test_blogentry.py
==============================================================================
--- (empty file)
+++ z3/zblog/trunk/browser/ftests/test_blogentry.py	Mon Feb 14 22:04:32 2005
@@ -0,0 +1,50 @@
+# $Id$
+
+import unittest
+from zope.app.tests.functional import BrowserTestCase
+
+class BlogEntryTest(BrowserTestCase):
+
+    def testAddBlogEntry(self):
+        response = self.publish(
+            '/+/AddBlog.html=blog',
+            basic='mgr:mgrpw',
+            form={'field.title' : u'Blog title',
+                  'field.description' : u'Blog description',
+                  'UPDATE_SUBMIT' : 'Add'})
+        self.assertEqual(response.getStatus(), 302)
+        self.assertEqual(response.getHeader('Location'),
+                         'http://localhost/@@contents.html')
+
+        response = self.publish(
+            '/blog/+/AddBlogEntry.html=bentry',
+            basic='mgr:mgrpw',
+            form={'field.title' : u'Blog entry title',
+                  'field.content' : u'Blog entry content',
+                  'field.summary' : u'Blog entry summary',
+                  'UPDATE_SUBMIT' : 'Add'})
+        self.assertEqual(response.getStatus(), 302)
+        # id of blog entry can be changed if there is adapter
+        # for Blog that implements INameChooser
+        self.assertEqual(response.getHeader('Location'),
+                         'http://localhost/blog/@@contents.html')
+
+    def testBlogEntryDetails(self):
+        self.testAddBlogEntry()
+
+        response = self.publish('/blog/bentry/@@details.html',
+                                basic='mgr:mgrpw')
+        body = response.getBody()
+        self.checkForBrokenLinks(body, '/blog/bentry/@@details.html',
+                                 basic='mgr:mgrpw')
+
+        self.assert_('Blog entry title' in body)
+        self.assert_('Blog entry content' in body)
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(BlogEntryTest),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Added: z3/zblog/trunk/configure.zcml
==============================================================================
--- (empty file)
+++ z3/zblog/trunk/configure.zcml	Mon Feb 14 22:04:32 2005
@@ -0,0 +1,115 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope">
+
+  <permission
+      id="blog.View"
+      title="View Blog and Blog entries"
+      description="View blog and blog entries"
+      />
+  <permission
+      id="blog.Add"
+      title="Add Blog Entry"
+      description="Add Blog Entry"
+      />
+  <permission
+      id="blog.Edit"
+      title="Edit Blog Entry"
+      description="Edit Blog Entry"
+      />
+  <permission
+      id="blog.Delete"
+      title="Delete Blog Entry"
+      description="Delete Blog Entry"
+      />
+
+  <role
+      id="blog.User"
+      title="Blog User"
+      description="Blog visitors, who can view blog/blog entries and comment"
+      />
+  <role
+      id="blog.Poster"
+      title="Blog Poster"
+      description="The Poster can add and view blog entries"
+      />
+  <role
+      id="blog.Manager"
+      title="Blog Manager"
+      description="Manager can do everything"
+      />
+
+  <grant
+      permission="blog.View"
+      role="blog.User"
+      />
+  <grant
+      permission="blog.Add"
+      role="blog.Poster"
+      />
+  <grant
+      permission="blog.Edit"
+      role="blog.Manager"
+      />
+  <grant
+      permission="blog.Delete"
+      role="blog.Manager"
+      />
+
+  <interface
+      interface=".interfaces.IBlog"
+      type="zope.app.content.interfaces.IContentType"
+      />
+
+  <content class=".blog.Blog">
+    <implements
+	interface="zope.app.annotation.interfaces.IAttributeAnnotatable"
+	/>
+    <implements
+	interface="zope.app.container.interfaces.IContentContainer"
+	/>
+    <factory
+	id="blog.Blog"
+	description="Blog"
+	/>
+    <require
+	permission="blog.View"
+	interface=".interfaces.IBlog"
+	/>
+    <require
+	permission="blog.Edit"
+	set_schema=".interfaces.IBlog"
+	/>
+  </content>
+
+  <interface
+      interface=".interfaces.IBlogEntry"
+      type="zope.app.content.interfaces.IContentType"
+      />
+
+  <content class=".blogentry.BlogEntry">
+    <implements
+	interface="zope.app.annotation.interfaces.IAttributeAnnotatable"
+	/>
+    <factory
+	id="blogentry.BlogEntry"
+	description="Blog Entry"
+	/>
+    <require
+	permission="blog.View"
+	interface=".interfaces.IBlogEntry"
+	/>
+    <require
+	permission="blog.Add"
+	set_schema=".interfaces.IBlogEntry"
+	/>
+  </content>
+
+  <adapter
+      for="zblog.interfaces.IBlog"
+      factory=".blog.BlogNameChooser"
+      provides="zope.app.container.interfaces.INameChooser"
+      />
+
+  <include package=".browser" />
+
+</configure>

Added: z3/zblog/trunk/interfaces.py
==============================================================================
--- (empty file)
+++ z3/zblog/trunk/interfaces.py	Mon Feb 14 22:04:32 2005
@@ -0,0 +1,56 @@
+from zope.interface import Interface
+from zope.schema import Text, TextLine, Field
+
+from zope.app.container.constraints import ContainerTypesConstraint
+from zope.app.container.constraints import ItemTypePrecondition
+from zope.app.container.interfaces import IContained, IContainer
+
+class IBlogEntry(Interface):
+    """Interface for blog entry objects."""
+
+    title = TextLine(
+        title = u"Blog entry title",
+        description = u"Blog entry title.",
+        default = u"",
+        required = True)
+
+    content = Text(
+        title = u"Blog entry content",
+        description = u"Blog entry content.",
+        default = u"",
+        required = True)
+
+    summary = Text(
+        title = u"Summary",
+        description = u"An excerpt from content.""",
+        default = u"",
+        max_length = 250,
+        required = False)
+
+
+class IBlog(IContainer):
+    """The blog container which can contain IBlogEntry objects."""
+
+    def __setitem__(name, object):
+        """Add a IBlogEntry object."""
+
+    __setitem__.precondition = ItemTypePrecondition(IBlogEntry)
+
+    title = TextLine(
+        title = u"Blog title",
+        description = u"Blog description",
+        default = u"",
+        required = True)
+
+    description = Text(
+        title = u"Description",
+        description = u"A detailed description of the blog's contents.",
+        default = u"",
+        required = False)
+
+
+class IBlogEntryContained(IContained):
+    """Interface that specifies which type of objects can contain blog
+    entries."""
+
+    __parent__ = Field(constraint = ContainerTypesConstraint(IBlog))

Added: z3/zblog/trunk/tests/__init__.py
==============================================================================
--- (empty file)
+++ z3/zblog/trunk/tests/__init__.py	Mon Feb 14 22:04:32 2005
@@ -0,0 +1 @@
+#

Added: z3/zblog/trunk/tests/test_blog.py
==============================================================================
--- (empty file)
+++ z3/zblog/trunk/tests/test_blog.py	Mon Feb 14 22:04:32 2005
@@ -0,0 +1,22 @@
+# $Id$
+
+import unittest
+from zope.testing.doctestunit import DocTestSuite
+
+from zope.app.container.tests.test_icontainer import TestSampleContainer
+
+from zblog.blog import Blog
+
+class Test(TestSampleContainer):
+
+    def makeTestObject(self):
+        return Blog()
+
+def test_suite():
+    return unittest.TestSuite((
+        DocTestSuite('zblog.blog'),
+        unittest.makeSuite(Test),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Added: z3/zblog/trunk/tests/test_blogentry.py
==============================================================================
--- (empty file)
+++ z3/zblog/trunk/tests/test_blogentry.py	Mon Feb 14 22:04:32 2005
@@ -0,0 +1,14 @@
+# $Id$
+
+import unittest
+from zope.testing.doctestunit import DocTestSuite
+
+from zblog.blogentry import BlogEntry
+
+def test_suite():
+    return unittest.TestSuite((
+        DocTestSuite('zblog.blogentry'),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')


More information about the z3-checkins mailing list