[Zope-CVS] SVN: messageboard/trunk/step06/ Added internationalization and localization step.

Stephan Richter srichter at cosmos.phy.tufts.edu
Tue May 18 15:12:13 EDT 2004


Log message for revision 24810:
Added internationalization and localization step.



-=-
Added: messageboard/trunk/step06/__init__.py
===================================================================
--- messageboard/trunk/step06/__init__.py	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/__init__.py	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1 @@
+# Make it a Python package

Added: messageboard/trunk/step06/browser/__init__.py
===================================================================
--- messageboard/trunk/step06/browser/__init__.py	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/browser/__init__.py	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1 @@
+# Make it a Python package

Added: messageboard/trunk/step06/browser/configure.zcml
===================================================================
--- messageboard/trunk/step06/browser/configure.zcml	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/browser/configure.zcml	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,123 @@
+<configure
+    xmlns="http://namespaces.zope.org/browser"
+    xmlns:zope="http://namespaces.zope.org/zope">
+
+  <addform
+      label="Add Message Board"
+      name="AddMessageBoard.html"
+      schema="book.messageboard.interfaces.IMessageBoard"
+      content_factory="book.messageboard.messageboard.MessageBoard"
+      fields="description"
+      permission="zope.ManageContent"
+      />
+
+  <addMenuItem
+      class="book.messageboard.messageboard.MessageBoard"
+      title="Message Board"
+      description="A Message Board"
+      permission="zope.ManageContent"
+      view="AddMessageBoard.html" 
+      />
+
+  <editform
+      schema="book.messageboard.interfaces.IMessageBoard"
+      for="book.messageboard.interfaces.IMessageBoard"
+      label="Change Message Board"
+      name="edit.html"
+      permission="zope.ManageContent"
+      menu="zmi_views" title="Edit" 
+      />
+
+  <containerViews
+      for="book.messageboard.interfaces.IMessageBoard"
+      index="book.messageboard.View"
+      contents="book.messageboard.Edit"
+      add="book.messageboard.Add"
+      />
+
+  <page
+      name="thread.html"
+      for="book.messageboard.interfaces.IMessageBoard"
+      class=".thread.Thread"
+      template="thread.pt"
+      permission="book.messageboard.View"
+      menu="zmi_views" title="Thread"/>
+
+  <defaultView
+      for="book.messageboard.interfaces.IMessageBoard"
+      name="thread.html"/>
+
+  <icon
+      name="zmi_icon"
+      for="book.messageboard.interfaces.IMessageBoard"
+      file="messageboard.png" />
+
+  <addform
+      label="Add Message"
+      name="AddMessage.html"
+      schema="book.messageboard.interfaces.IMessage"
+      content_factory="book.messageboard.message.Message"
+      fields="title body"
+      permission="book.messageboard.Add"
+      />
+
+  <addMenuItem
+      class="book.messageboard.message.Message"
+      title="Message"
+      description="A Message"
+      permission="book.messageboard.Add"
+      view="AddMessage.html" 
+      />
+
+  <editform
+      schema="book.messageboard.interfaces.IMessage"
+      for="book.messageboard.interfaces.IMessage"
+      label="Change Message"
+      fields="title body"
+      name="edit.html"
+      permission="book.messageboard.Edit"
+      menu="zmi_views" title="Edit" 
+      />
+
+  <containerViews
+      for="book.messageboard.interfaces.IMessage"
+      index="book.messageboard.View"
+      contents="book.messageboard.Edit"
+      add="book.messageboard.Add"
+      />
+
+  <page
+      name="details.html"
+      for="book.messageboard.interfaces.IMessage"
+      class=".message.MessageDetails"
+      template="details.pt"
+      permission="book.messageboard.View"
+      menu="zmi_views" title="Preview"/>
+
+  <defaultView
+      for="book.messageboard.interfaces.IMessage"
+      name="details.html"/>
+
+  <page
+      name="thread.html"
+      for="book.messageboard.interfaces.IMessage"
+      class=".thread.Thread"
+      template="thread.pt"
+      permission="book.messageboard.View"
+      menu="zmi_views" title="Thread"/>
+
+  <icon
+      name="zmi_icon"
+      for="book.messageboard.interfaces.IMessage"
+      file="message.png" />
+
+
+  <zope:view
+      type="zope.publisher.interfaces.browser.IBrowserRequest"
+      for="book.messageboard.interfaces.IHTML"
+      provides="zope.app.form.interfaces.IInputWidget"
+      factory=".widgets.HTMLSourceWidget"
+      permission="zope.Public"
+      />
+
+</configure>

Added: messageboard/trunk/step06/browser/details.pt
===================================================================
--- messageboard/trunk/step06/browser/details.pt	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/browser/details.pt	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,38 @@
+<html metal:use-macro="views/standard_macros/page">
+  <body>
+    <div metal:fill-slot="body" i18n:domain="messageboard">
+
+      <h1 i18n:translate="">Message Details</h1>
+
+        <div class="row">
+            <div class="label" i18n:translate="">Title</div>
+            <div class="field" tal:content="context/title" />
+        </div>
+
+        <div class="row">
+            <div class="label" i18n:translate="">Author</div>
+            <div class="field" tal:content="view/author"/>
+        </div>
+
+        <div class="row">
+            <div class="label" i18n:translate="">Date/Time</div>
+            <div class="field" tal:content="view/modified"/>
+        </div>
+
+        <div class="row">
+            <div class="label" i18n:translate="">Parent</div>
+            <div class="field" tal:define="info view/parent_info">
+              <a href="../" 
+                  tal:condition="info"
+                  tal:content="info/title" />
+            </div>
+        </div>
+
+        <div class="row">
+            <div class="label" i18n:translate="">Body</div>
+            <div class="field" tal:content="structure context/body"/>
+        </div>
+
+    </div>
+  </body>
+</html>

Added: messageboard/trunk/step06/browser/ftests/__init__.py
===================================================================
--- messageboard/trunk/step06/browser/ftests/__init__.py	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/browser/ftests/__init__.py	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1 @@
+# Make it a Python package

Added: messageboard/trunk/step06/browser/ftests/test_message.py
===================================================================
--- messageboard/trunk/step06/browser/ftests/test_message.py	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/browser/ftests/test_message.py	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,61 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Message Functional Tests
+
+$Id$
+"""
+import unittest
+from zope.app.tests.functional import BrowserTestCase
+
+class MessageTest(BrowserTestCase):
+
+    def testAddMessage(self):
+        response = self.publish(
+            '/+/AddMessageBoard.html=board',
+            basic='mgr:mgrpw',
+            form={'field.description': u'Message Board',
+                  'UPDATE_SUBMIT': 'Add'})
+        self.assertEqual(response.getStatus(), 302)
+        self.assertEqual(response.getHeader('Location'),
+                         'http://localhost/@@contents.html')
+        response = self.publish(
+            '/board/+/AddMessage.html=msg1',
+            basic='mgr:mgrpw',
+            form={'field.title': u'Message 1',
+                  'field.body': u'Body',
+                  'UPDATE_SUBMIT': 'Add'})
+        self.assertEqual(response.getStatus(), 302)
+        self.assertEqual(response.getHeader('Location'),
+                         'http://localhost/board/@@contents.html')
+       
+    def testMessageDetails(self):
+        self.testAddMessage()
+        response = self.publish('/board/msg1/@@details.html',
+                                basic='mgr:mgrpw')
+        body = response.getBody()
+        self.checkForBrokenLinks(body, '/board/msg1/@@details.html',
+                                 basic='mgr:mgrpw')
+        
+        self.assert_(body.find('Message Details') > 0)
+        self.assert_(body.find('Message 1') > 0)
+        self.assert_(body.find('Body') > 0)
+        
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(MessageTest),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Added: messageboard/trunk/step06/browser/message.png
===================================================================
(Binary files differ)


Property changes on: messageboard/trunk/step06/browser/message.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: messageboard/trunk/step06/browser/message.py
===================================================================
--- messageboard/trunk/step06/browser/message.py	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/browser/message.py	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,51 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Browser Views for IMessage
+
+$Id: message.py,v 1.3 2003/12/13 17:24:36 srichter Exp $
+"""
+from zope.i18n import MessageIDFactory
+
+from zope.app import zapi
+from zope.app.dublincore.interfaces import ICMFDublinCore
+
+from book.messageboard.interfaces import IMessage
+
+_ = MessageIDFactory('messageboard')
+
+class MessageDetails:
+
+    def author(self):
+        """Get user who last modified the Message."""
+        creators = zapi.getAdapter(self.context, ICMFDublinCore).creators
+        if not creators:
+            return _('unknown')
+        return creators[0]
+
+    def modified(self):
+        """Get last modification date."""
+        date = zapi.getAdapter(self.context, ICMFDublinCore).modified
+        if date is None:
+            date = zapi.getAdapter(self.context, ICMFDublinCore).created
+        if date is None:
+            return ''
+        formatter = self.request.locale.dates.getFormatter('dateTime', 'short')
+        return formatter.format(date)
+
+    def parent_info(self):
+        """Get the parent of the message"""
+        parent = zapi.getParent(self.context)
+        if not IMessage.providedBy(parent):
+            return None
+        return {'name': zapi.name(parent), 'title': parent.title}

Added: messageboard/trunk/step06/browser/messageboard.png
===================================================================
(Binary files differ)


Property changes on: messageboard/trunk/step06/browser/messageboard.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: messageboard/trunk/step06/browser/subthread.pt
===================================================================
--- messageboard/trunk/step06/browser/subthread.pt	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/browser/subthread.pt	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,8 @@
+<ul>
+  <li tal:repeat="item view/listContentInfo">
+    <a href="" 
+        tal:attributes="href item/url"
+        tal:content="item/title">Message 1</a>
+    <div tal:replace="structure item/thread"/>
+  </li>
+</ul>
\ No newline at end of file

Added: messageboard/trunk/step06/browser/tests/__init__.py
===================================================================
--- messageboard/trunk/step06/browser/tests/__init__.py	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/browser/tests/__init__.py	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1 @@
+# Make it a Python package

Added: messageboard/trunk/step06/browser/tests/test_widgets.py
===================================================================
--- messageboard/trunk/step06/browser/tests/test_widgets.py	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/browser/tests/test_widgets.py	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,76 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""HTMLSourceWidget Tests
+
+$Id: test_widgets.py,v 1.1 2003/06/10 14:40:44 srichter Exp $
+"""
+import unittest
+from zope.app.form.browser.tests.test_textareawidget import TextAreaWidgetTest
+from book.messageboard.browser.widgets import HTMLSourceWidget
+from book.messageboard.fields import HTML
+
+class HTMLSourceWidgetTest(TextAreaWidgetTest):
+
+    _FieldFactory = HTML
+    _WidgetFactory = HTMLSourceWidget
+
+
+    def test_AllowedTagsConvert(self):
+        widget = self._widget
+        widget.context.allowed_tags=('h1','pre')
+        self.assertEqual(u'<h1>Blah</h1>',
+                         widget._toFieldValue(u'<h1>Blah</h1>')) 
+        self.assertEqual(u'<pre>Blah</pre>',
+                         widget._toFieldValue(u'<pre>Blah</pre>') )
+        self.assertEqual(u'<h1><pre>Blah</pre></h1>',
+                         widget._toFieldValue(u'<h1><pre>Blah</pre></h1>')) 
+        self.assertEqual(u'<h1 attr=".">Blah</h1>',
+                         widget._toFieldValue(u'<h1 attr=".">Blah</h1>')) 
+
+        self.assertEqual(u'Blah',
+                         widget._toFieldValue(u'<h2>Blah</h2>')) 
+        self.assertEqual(u'<pre>Blah</pre>',
+                         widget._toFieldValue(u'<h2><pre>Blah</pre></h2>')) 
+        self.assertEqual(u'Blah',
+                         widget._toFieldValue(u'<h2 a="b">Blah</h2>')) 
+
+
+    def test_ForbiddenTagsConvert(self):
+        widget = self._widget
+        widget.context.forbidden_tags=('h2','pre')
+
+        self.assertEqual(u'<h1>Blah</h1>',
+                         widget._toFieldValue(u'<h1>Blah</h1>')) 
+        self.assertEqual(u'<h1 a="b">Blah</h1>',
+                         widget._toFieldValue(u'<h1 a="b">Blah</h1>')) 
+
+        self.assertEqual(u'Blah',
+                         widget._toFieldValue(u'<h2>Blah</h2>')) 
+        self.assertEqual(u'Blah',
+                         widget._toFieldValue(u'<pre>Blah</pre>')) 
+        self.assertEqual(u'Blah',
+                         widget._toFieldValue(u'<h2><pre>Blah</pre></h2>')) 
+        self.assertEqual(u'Blah',
+                         widget._toFieldValue(u'<h2><pre>Blah</pre></h2>')) 
+        self.assertEqual(u'<h1>Blah</h1>',
+                         widget._toFieldValue(u'<h1><pre>Blah</pre></h1>')) 
+
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(HTMLSourceWidgetTest),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Added: messageboard/trunk/step06/browser/thread.pt
===================================================================
--- messageboard/trunk/step06/browser/thread.pt	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/browser/thread.pt	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,11 @@
+<html metal:use-macro="views/standard_macros/view">
+  <body>
+    <div metal:fill-slot="body" i18n:domain="messageboard">
+
+      <h1 i18n:translate="">Discussion Thread</h1>
+
+      <div tal:replace="structure view/subthread" />
+
+    </div>
+  </body>
+</html>

Added: messageboard/trunk/step06/browser/thread.py
===================================================================
--- messageboard/trunk/step06/browser/thread.py	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/browser/thread.py	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,42 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Browser View for the sub-thread of a Message or MessageBoard
+
+$Id$
+"""
+from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
+from book.messageboard.interfaces import IMessage
+
+class Thread:
+
+    def __init__(self, context, request, base_url=''):
+        self.context = context
+        self.request = request
+        self.base_url = base_url
+
+    def listContentInfo(self):
+        children = []
+        for name, child in self.context.items():
+            if IMessage.providedBy(child):
+                info = {}
+                info['title'] = child.title
+                url = self.base_url + name + '/'
+                info['url'] = url + '@@thread.html'
+                thread = Thread(child, self.request, url)
+                info['thread'] = thread.subthread()
+                children.append(info)
+        return children
+
+    subthread = ViewPageTemplateFile('subthread.pt')
+

Added: messageboard/trunk/step06/browser/widgets.py
===================================================================
--- messageboard/trunk/step06/browser/widgets.py	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/browser/widgets.py	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,38 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Module containing custom widget definitions.
+
+$Id: widgets.py,v 1.1 2003/06/10 14:40:44 srichter Exp $
+"""
+import re
+from zope.app.form.browser import TextAreaWidget
+from book.messageboard.fields import forbidden_regex, allowed_regex
+
+class HTMLSourceWidget(TextAreaWidget):
+
+    def _toFieldValue(self, input):
+        input = super(HTMLSourceWidget, self)._toFieldValue(input)
+
+        if self.context.forbidden_tags:
+            regex = forbidden_regex %'|'.join(
+                self.context.forbidden_tags)
+            input = re.sub(regex, '', input)
+
+        if self.context.allowed_tags:
+            regex = allowed_regex %'|'.join(
+                self.context.allowed_tags)
+            input = re.sub(regex, '', input)
+
+        return input
+

Added: messageboard/trunk/step06/configure.zcml
===================================================================
--- messageboard/trunk/step06/configure.zcml	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/configure.zcml	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,86 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:i18n="http://namespaces.zope.org/i18n"
+    i18n_domain="messageboard">
+
+  <permission
+      id="book.messageboard.View"
+      title="View Message Board and Messages"
+      description="View the Message Board and all its content."
+      />
+  <permission
+      id="book.messageboard.Add"
+      title="Add Message"
+      description="Add Message."
+      />
+  <permission
+      id="book.messageboard.Edit"
+      title="Edit Messages"
+      description="Edit Messages."
+      />
+  <permission
+      id="book.messageboard.Delete"
+      title="Delete Message"
+      description="Delete Message."
+      />
+
+  <interface 
+      interface=".interfaces.IMessageBoard" 
+      type="zope.app.content.interfaces.IContentType"
+      /> 
+
+  <content class=".messageboard.MessageBoard">
+    <implements
+        interface="zope.app.annotation.interfaces.IAttributeAnnotatable"
+        />
+    <implements
+        interface="zope.app.container.interfaces.IContentContainer" 
+        />
+    <factory
+        id="book.messageboard.MessageBoard"
+        description="Message Board" 
+        />
+    <require
+        permission="book.messageboard.View"
+        interface=".interfaces.IMessageBoard"
+        />
+    <require
+        permission="book.messageboard.Edit"
+        set_schema=".interfaces.IMessageBoard"
+        />
+  </content>
+
+  <interface 
+      interface=".interfaces.IMessage" 
+      type="zope.app.content.interfaces.IContentType"
+      /> 
+
+  <content class=".message.Message">
+    <implements
+        interface="zope.app.annotation.interfaces.IAttributeAnnotatable"
+        />
+    <implements
+        interface="zope.app.container.interfaces.IContentContainer" 
+        />
+    <require
+        permission="book.messageboard.View"
+        interface=".interfaces.IMessage"
+        />
+    <require
+        permission="book.messageboard.Add"
+        set_schema=".interfaces.IMessage"
+        />
+  </content>
+
+  <adapter
+      factory=".message.MessageSized"
+      provides="zope.app.size.interfaces.ISized"
+      for=".interfaces.IMessage"
+      />
+
+  <i18n:registerTranslations directory="locales" />
+
+  <include package=".browser" />
+  <include file="security.zcml" />
+
+</configure>

Added: messageboard/trunk/step06/fields.py
===================================================================
--- messageboard/trunk/step06/fields.py	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/fields.py	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,56 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Module containing custom field definitions.
+
+$Id$
+"""
+import re
+
+from zope.i18n import MessageIDFactory
+from zope.schema import Text
+from zope.schema.interfaces import ValidationError
+
+_ = MessageIDFactory('messageboard')
+
+forbidden_regex = r'</?(?:%s).*?/?>'
+allowed_regex = r'</??(?!%s)[a-zA-Z0-9]*? ?(?:[a-z0-9]*?=?".*?")*/??>'
+
+class ForbiddenTags(ValidationError):
+    __doc__ = _("Forbidden HTML Tags used.")
+
+
+class HTML(Text):
+  
+    allowed_tags = ()
+    forbidden_tags = ()
+
+    def __init__(self, allowed_tags=(), forbidden_tags=(), **kw):
+        self.allowed_tags = allowed_tags
+        self.forbidden_tags = forbidden_tags
+        super(HTML, self).__init__(**kw)
+
+    def _validate(self, value):
+        super(HTML, self)._validate(value)
+
+        if self.forbidden_tags:
+            regex = forbidden_regex %'|'.join(self.forbidden_tags)
+            if re.findall(regex, value):
+                raise ForbiddenTags(value, self.forbidden_tags)
+
+        if self.allowed_tags:
+            regex = allowed_regex %'|'.join(self.allowed_tags)
+            if re.findall(regex, value):
+                raise ForbiddenTags(value, self.allowed_tags)
+
+

Added: messageboard/trunk/step06/interfaces.py
===================================================================
--- messageboard/trunk/step06/interfaces.py	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/interfaces.py	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,98 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Message Board Interfaces
+
+Interfaces for the Zope 3 based Message Board Package
+
+$Id$
+"""
+from zope.i18n import MessageIDFactory
+from zope.interface import classImplements
+from zope.schema import Text, TextLine, Field, Tuple
+from zope.schema.interfaces import IText
+
+from zope.app.container.constraints import ContainerTypesConstraint
+from zope.app.container.constraints import ItemTypePrecondition
+from zope.app.container.interfaces import IContainer
+from zope.app.file.interfaces import IFile
+
+from fields import HTML
+
+_ = MessageIDFactory('messageboard')
+
+
+class IMessage(IContainer):
+    """A message object. It can contain its own responses."""
+
+    def __setitem__(name, object):
+        """Add a IMessage object."""
+
+    title = TextLine(
+        title=_("Title/Subject"),
+        description=_("Title and/or subject of the message."),
+        default=u"",
+        required=True)
+
+    body = HTML(
+        title=_("Message Body"),
+        description=_("This is the actual message. Type whatever!"),
+        default=u"",
+        allowed_tags=('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'img', 'a',
+                      'br', 'b', 'i', 'u', 'em', 'sub', 'sup',
+                      'table', 'tr', 'td', 'th', 'code', 'pre',
+                      'center', 'div', 'span', 'p', 'font', 'ol',
+                      'ul', 'li', 'q', 's', 'strong'),
+        required=False)
+
+
+class IMessageBoard(IContainer):
+    """The message board is the base object for our package. It can only
+    contain IMessage objects."""
+
+    def __setitem__(name, object):
+        """Add a IMessage object."""
+
+    __setitem__.precondition = ItemTypePrecondition(IMessage)
+
+    description = Text(
+        title=_("Description"),
+        description=_("A detailed description of the content of the board."),
+        default=u"",
+        required=False)
+
+
+IMessage['__setitem__'].setTaggedValue('precondition',
+                                       ItemTypePrecondition(IMessage, IFile))
+IMessage.setTaggedValue('__parent__', Field(
+    constraint=ContainerTypesConstraint(IMessageBoard, IMessage)))
+
+
+class IHTML(IText):
+    """A text field that handles HTML input."""
+
+    allowed_tags = Tuple(
+        title=_("Allowed HTML Tags"),
+        description=_("""\
+        Only listed tags can be used in the value of the field.
+        """),
+        required=False)
+
+    forbidden_tags = Tuple(
+        title=_("Forbidden HTML Tags"),
+        description=_("""\
+        Listed tags cannot be used in the value of the field.
+        """),
+        required=False)
+
+classImplements(HTML, IHTML)

Added: messageboard/trunk/step06/locales/de/LC_MESSAGES/messageboard.mo
===================================================================
(Binary files differ)


Property changes on: messageboard/trunk/step06/locales/de/LC_MESSAGES/messageboard.mo
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: messageboard/trunk/step06/locales/de/LC_MESSAGES/messageboard.po
===================================================================
--- messageboard/trunk/step06/locales/de/LC_MESSAGES/messageboard.po	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/locales/de/LC_MESSAGES/messageboard.po	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,211 @@
+# translation of messageboard.po to German
+# translation of messageboard.po to English US
+# Copyright (C) YEAR ORGANIZATION.
+# Stephan Richter <stephan.richter at tufts.edu>, 2003, 2004.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: messageboard\n"
+"POT-Creation-Date: Tue May 18 14:15:19 2004\n"
+"PO-Revision-Date: 2004-05-18 14:26-0400\n"
+"Last-Translator: Stephan Richter <stephan.richter at tufts.edu>\n"
+"Language-Team: German <zope3-dev at zope.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: pygettext.py 1.4\n"
+"X-Generator: KBabel 1.3\n"
+
+#: src/book/messageboard/browser/configure.zcml:14
+msgid "A Message Board"
+msgstr "Ein Anzeigebrett"
+
+#: src/book/messageboard/browser/configure.zcml:22
+msgid "Change Message Board"
+msgstr "Anzeigebrett ändern"
+
+#: src/book/messageboard/browser/configure.zcml:22
+#: src/book/messageboard/browser/configure.zcml:72
+msgid "Edit"
+msgstr "Bearbeiten"
+
+#: src/book/messageboard/browser/configure.zcml:38
+#: src/book/messageboard/browser/configure.zcml:101
+msgid "Thread"
+msgstr "Unterhaltungsfaden"
+
+#: src/book/messageboard/browser/configure.zcml:5
+msgid "Add Message Board"
+msgstr "Anzeigebrett hinzufügen"
+
+#: src/book/messageboard/browser/configure.zcml:64
+msgid "A Message"
+msgstr "Eine Anzeige"
+
+#: src/book/messageboard/browser/configure.zcml:64
+msgid "Message"
+msgstr "Anzeige"
+
+#: src/book/messageboard/browser/configure.zcml:72
+msgid "Change Message"
+msgstr "Anzeige ändern"
+
+#: src/book/messageboard/browser/configure.zcml:89
+msgid "Preview"
+msgstr "Vorschau"
+
+#: src/book/messageboard/browser/details.pt:13
+msgid "Author"
+msgstr "Autor"
+
+#: src/book/messageboard/browser/details.pt:18
+msgid "Date/Time"
+msgstr "Datum/Zeit"
+
+#: src/book/messageboard/browser/details.pt:23
+msgid "Parent"
+msgstr "Vorfahre"
+
+#: src/book/messageboard/browser/details.pt:32
+msgid "Body"
+msgstr "Inhalt"
+
+#: src/book/messageboard/browser/details.pt:5
+msgid "Message Details"
+msgstr "Anzeigedetails"
+
+#: src/book/messageboard/browser/details.pt:8
+msgid "Title"
+msgstr "Titel"
+
+#: src/book/messageboard/browser/message.py:33
+msgid "unknown"
+msgstr "unbekannt"
+
+#: src/book/messageboard/browser/thread.pt:5
+msgid "Discussion Thread"
+msgstr "Diskussions-Faden"
+
+#: src/book/messageboard/configure.zcml:10
+msgid "Add Message."
+msgstr "Nachricht hinzufügen."
+
+#: src/book/messageboard/configure.zcml:10
+#: src/book/messageboard/browser/configure.zcml:55
+msgid "Add Message"
+msgstr "Anzeige hinzufügen"
+
+#: src/book/messageboard/configure.zcml:15
+msgid "Edit Messages."
+msgstr "Anzeigen bearbeiten."
+
+#: src/book/messageboard/configure.zcml:15
+msgid "Edit Messages"
+msgstr "Anzeigen bearbeiten"
+
+#: src/book/messageboard/configure.zcml:20
+msgid "Delete Message."
+msgstr "Anzeigen löschen."
+
+#: src/book/messageboard/configure.zcml:20
+msgid "Delete Message"
+msgstr "Anzeigen löschen"
+
+#: src/book/messageboard/configure.zcml:38
+#: src/book/messageboard/browser/configure.zcml:14
+msgid "Message Board"
+msgstr "Anzeigenbrett"
+
+#: src/book/messageboard/configure.zcml:5
+msgid "View the Message Board and all its content."
+msgstr "Das Anzeigebrett und seine Inhalte anschauen."
+
+#: src/book/messageboard/configure.zcml:5
+msgid "View Message Board and Messages"
+msgstr "Anzeigebrett und Anzeigen ansehen"
+
+#: src/book/messageboard/fields.py:30
+msgid "Forbidden HTML Tags used."
+msgstr "Verbotene HTML Tags wurden verwendet."
+
+#: src/book/messageboard/interfaces.py:42
+msgid "Title/Subject"
+msgstr "Titel/Thema"
+
+#: src/book/messageboard/interfaces.py:43
+msgid "Title and/or subject of the message."
+msgstr "Titel und/oder Thema der Anzeige."
+
+#: src/book/messageboard/interfaces.py:48
+msgid "Message Body"
+msgstr "Text der Anzeige"
+
+#: src/book/messageboard/interfaces.py:49
+msgid "This is the actual message. Type whatever!"
+msgstr "Dies ist die Anzeige selbst. Schreib was immer Du willst!"
+
+#: src/book/messageboard/interfaces.py:69
+msgid "Description"
+msgstr "Beschreibung"
+
+#: src/book/messageboard/interfaces.py:70
+msgid "A detailed description of the content of the board."
+msgstr "Eine detailierte Beschreibung des Brett-Inhaltes."
+
+#: src/book/messageboard/interfaces.py:85
+msgid "Allowed HTML Tags"
+msgstr "Erlaubte HTML Tags"
+
+#: src/book/messageboard/interfaces.py:86
+msgid ""
+"        Only listed tags can be used in the value of the field.\n"
+"        "
+msgstr ""
+"        Aufgelistete Tags können im Inhalt dieses Feldes benutzt werden.\n"
+"        "
+
+#: src/book/messageboard/interfaces.py:92
+msgid "Forbidden HTML Tags"
+msgstr "Verbotene HTML Tags"
+
+#: src/book/messageboard/interfaces.py:93
+msgid ""
+"        Listed tags cannot be used in the value of the field.\n"
+"        "
+msgstr ""
+"        Aufgelistete Tags können nicht im Inhalt dieses Feldes benutzt "
+"werden.\n"
+"        "
+
+#: src/book/messageboard/message.py:137
+msgid "1 reply, 1 attachment"
+msgstr "1 Antwort, 1 Anhang"
+
+#: src/book/messageboard/message.py:139
+msgid "1 reply, ${attachments} attachments"
+msgstr "1 Antwort, $attachments} Anhänge"
+
+#: src/book/messageboard/message.py:141
+msgid "${messages} replies, 1 attachment"
+msgstr "${messages} Antworten, 1 Anhang"
+
+#: src/book/messageboard/message.py:143
+msgid "${messages} replies, ${attachments} attachments"
+msgstr "${messages} Antworten, ${attachments} Anhänge"
+
+#: src/book/messageboard/security.zcml:18
+msgid "Message Board Editor"
+msgstr "Anzeigebrett Editor"
+
+#: src/book/messageboard/security.zcml:18
+msgid "The Editor can edit and delete Messages."
+msgstr "Der Editor kann Anzeigen bearbeiten und löschen."
+
+#: src/book/messageboard/security.zcml:4
+msgid "Message Board User"
+msgstr "Anzeigenbrett Nutzer"
+
+#: src/book/messageboard/security.zcml:4
+msgid "Users that actually use the Message Board."
+msgstr "Nutzer die das Anzeigenbrett nutzen."
+

Added: messageboard/trunk/step06/locales/en/LC_MESSAGES/messageboard.mo
===================================================================
(Binary files differ)


Property changes on: messageboard/trunk/step06/locales/en/LC_MESSAGES/messageboard.mo
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: messageboard/trunk/step06/locales/en/LC_MESSAGES/messageboard.po
===================================================================
--- messageboard/trunk/step06/locales/en/LC_MESSAGES/messageboard.po	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/locales/en/LC_MESSAGES/messageboard.po	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,8 @@
+# This file contains no message ids because the messageboard's default
+# language is English
+msgid ""
+msgstr ""
+"Project-Id-Version: messageboard\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"

Added: messageboard/trunk/step06/locales/messageboard.pot
===================================================================
--- messageboard/trunk/step06/locales/messageboard.pot	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/locales/messageboard.pot	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,213 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+msgid ""
+msgstr ""
+"Project-Id-Version: Development/Unknown\n"
+"POT-Creation-Date: Tue May 18 14:15:19 2004\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
+"Language-Team: Zope 3 Developers <zope3-dev at zope.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: zope/app/translation_files/extract.py\n"
+
+#: src/book/messageboard/browser/configure.zcml:14
+msgid "A Message Board"
+msgstr ""
+
+#: src/book/messageboard/browser/configure.zcml:22
+msgid "Change Message Board"
+msgstr ""
+
+#: src/book/messageboard/browser/configure.zcml:22
+#: src/book/messageboard/browser/configure.zcml:72
+msgid "Edit"
+msgstr ""
+
+#: src/book/messageboard/browser/configure.zcml:38
+#: src/book/messageboard/browser/configure.zcml:101
+msgid "Thread"
+msgstr ""
+
+#: src/book/messageboard/browser/configure.zcml:5
+msgid "Add Message Board"
+msgstr ""
+
+#: src/book/messageboard/browser/configure.zcml:64
+msgid "A Message"
+msgstr ""
+
+#: src/book/messageboard/browser/configure.zcml:64
+msgid "Message"
+msgstr ""
+
+#: src/book/messageboard/browser/configure.zcml:72
+msgid "Change Message"
+msgstr ""
+
+#: src/book/messageboard/browser/configure.zcml:89
+msgid "Preview"
+msgstr ""
+
+#: src/book/messageboard/browser/details.pt:13
+msgid "Author"
+msgstr ""
+
+#: src/book/messageboard/browser/details.pt:18
+msgid "Date/Time"
+msgstr ""
+
+#: src/book/messageboard/browser/details.pt:23
+msgid "Parent"
+msgstr ""
+
+#: src/book/messageboard/browser/details.pt:32
+msgid "Body"
+msgstr ""
+
+#: src/book/messageboard/browser/details.pt:5
+msgid "Message Details"
+msgstr ""
+
+#: src/book/messageboard/browser/details.pt:8
+msgid "Title"
+msgstr ""
+
+#: src/book/messageboard/browser/message.py:33
+msgid "unknown"
+msgstr ""
+
+#: src/book/messageboard/browser/thread.pt:5
+msgid "Discussion Thread"
+msgstr ""
+
+#: src/book/messageboard/configure.zcml:10
+msgid "Add Message."
+msgstr ""
+
+#: src/book/messageboard/configure.zcml:10
+#: src/book/messageboard/browser/configure.zcml:55
+msgid "Add Message"
+msgstr ""
+
+#: src/book/messageboard/configure.zcml:15
+msgid "Edit Messages."
+msgstr ""
+
+#: src/book/messageboard/configure.zcml:15
+msgid "Edit Messages"
+msgstr ""
+
+#: src/book/messageboard/configure.zcml:20
+msgid "Delete Message."
+msgstr ""
+
+#: src/book/messageboard/configure.zcml:20
+msgid "Delete Message"
+msgstr ""
+
+#: src/book/messageboard/configure.zcml:38
+#: src/book/messageboard/browser/configure.zcml:14
+msgid "Message Board"
+msgstr ""
+
+#: src/book/messageboard/configure.zcml:5
+msgid "View the Message Board and all its content."
+msgstr ""
+
+#: src/book/messageboard/configure.zcml:5
+msgid "View Message Board and Messages"
+msgstr ""
+
+#: src/book/messageboard/fields.py:30
+msgid "Forbidden HTML Tags used."
+msgstr ""
+
+#: src/book/messageboard/interfaces.py:42
+msgid "Title/Subject"
+msgstr ""
+
+#: src/book/messageboard/interfaces.py:43
+msgid "Title and/or subject of the message."
+msgstr ""
+
+#: src/book/messageboard/interfaces.py:48
+msgid "Message Body"
+msgstr ""
+
+#: src/book/messageboard/interfaces.py:49
+msgid "This is the actual message. Type whatever!"
+msgstr ""
+
+#: src/book/messageboard/interfaces.py:69
+msgid "Description"
+msgstr ""
+
+#: src/book/messageboard/interfaces.py:70
+msgid "A detailed description of the content of the board."
+msgstr ""
+
+#: src/book/messageboard/interfaces.py:85
+msgid "Allowed HTML Tags"
+msgstr ""
+
+#: src/book/messageboard/interfaces.py:86
+msgid ""
+"        Only listed tags can be used in the value of the field.\n"
+"        "
+msgstr ""
+
+#: src/book/messageboard/interfaces.py:92
+msgid "Forbidden HTML Tags"
+msgstr ""
+
+#: src/book/messageboard/interfaces.py:93
+msgid ""
+"        Listed tags cannot be used in the value of the field.\n"
+"        "
+msgstr ""
+
+#: src/book/messageboard/message.py:137
+msgid "1 reply, 1 attachment"
+msgstr ""
+
+#: src/book/messageboard/message.py:139
+msgid "1 reply, ${attachments} attachments"
+msgstr ""
+
+#: src/book/messageboard/message.py:141
+msgid "${messages} replies, 1 attachment"
+msgstr ""
+
+#: src/book/messageboard/message.py:143
+msgid "${messages} replies, ${attachments} attachments"
+msgstr ""
+
+#: src/book/messageboard/security.zcml:18
+msgid "Message Board Editor"
+msgstr ""
+
+#: src/book/messageboard/security.zcml:18
+msgid "The Editor can edit and delete Messages."
+msgstr ""
+
+#: src/book/messageboard/security.zcml:4
+msgid "Message Board User"
+msgstr ""
+
+#: src/book/messageboard/security.zcml:4
+msgid "Users that actually use the Message Board."
+msgstr ""
+

Added: messageboard/trunk/step06/message.py
===================================================================
--- messageboard/trunk/step06/message.py	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/message.py	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,147 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Message Implementation
+
+An implementation of the Message using BTreeContainers as base.
+
+$Id: message.py,v 1.1 2003/06/07 11:24:48 srichter Exp $
+"""
+from zope.i18n import MessageIDFactory
+from zope.interface import implements
+from zope.app.container.btree import BTreeContainer
+from zope.app.size.interfaces import ISized
+
+from book.messageboard.interfaces import IMessage
+
+_ = MessageIDFactory('messageboard')
+
+
+class Message(BTreeContainer):
+    """A simple implementation of a message.
+
+    Make sure that the ``Message`` implements the ``IMessage`` interface:
+
+    >>> from zope.interface.verify import verifyClass
+    >>> verifyClass(IMessage, Message)
+    True
+
+    Here is an example of changing the title and description of the message:
+
+    >>> message = Message()
+    >>> message.title
+    u''
+    >>> message.body
+    u''
+    >>> message.title = u'Message Title'
+    >>> message.body = u'Message Body'
+    >>> message.title
+    u'Message Title'
+    >>> message.body
+    u'Message Body'
+    """
+    implements(IMessage)
+
+    # See book.messageboard.interfaces.IMessage
+    title = u''
+
+    # See book.messageboard.interfaces.IMessage
+    body = u''
+
+  
+class MessageSized(object):
+
+    implements(ISized)
+    __used_for__ = IMessage
+
+    def __init__(self, message):
+        self._message = message
+
+    def sizeForSorting(self):
+        """See ISized
+
+        Create the adapter first.
+
+        >>> size = MessageSized(Message())
+
+        Here are some examples of the expected output.
+
+        >>> size.sizeForSorting()
+        ('item', 0)
+        >>> size._message['msg1'] = Message()
+        >>> size.sizeForSorting()
+        ('item', 1)
+        >>> size._message['att1'] = object()
+        >>> size.sizeForSorting()
+        ('item', 2)
+        """
+        return ('item', len(self._message))
+
+    def sizeForDisplay(self):
+        """See ISized
+
+        Creater the adapter first.
+
+        >>> size = MessageSized(Message())
+
+        Here are some examples of the expected output.
+
+        >>> str = size.sizeForDisplay()
+        >>> str
+        u'${messages} replies, ${attachments} attachments'
+        >>> 'msgs: %(messages)s, atts: %(attachments)s' %str.mapping
+        'msgs: 0, atts: 0'
+        >>> size._message['msg1'] = Message()
+        >>> str = size.sizeForDisplay()
+        >>> str
+        u'1 reply, ${attachments} attachments'
+        >>> 'msgs: %(messages)s, atts: %(attachments)s' %str.mapping
+        'msgs: 1, atts: 0'
+        >>> size._message['att1'] = object()
+        >>> str = size.sizeForDisplay()
+        >>> str
+        u'1 reply, 1 attachment'
+        >>> 'msgs: %(messages)s, atts: %(attachments)s' %str.mapping
+        'msgs: 1, atts: 1'
+        >>> size._message['msg2'] =  Message()
+        >>> str = size.sizeForDisplay()
+        >>> str
+        u'${messages} replies, 1 attachment'
+        >>> 'msgs: %(messages)s, atts: %(attachments)s' %str.mapping
+        'msgs: 2, atts: 1'
+        >>> size._message['att2'] = object()
+        >>> str = size.sizeForDisplay()
+        >>> str
+        u'${messages} replies, ${attachments} attachments'
+        >>> 'msgs: %(messages)s, atts: %(attachments)s' %str.mapping
+        'msgs: 2, atts: 2'
+        """
+        messages = 0
+        for obj in self._message.values():
+            if IMessage.providedBy(obj):
+                messages += 1
+
+        attachments = len(self._message)-messages
+
+        if messages == 1 and attachments == 1: 
+            size = _('1 reply, 1 attachment')
+        elif messages == 1 and attachments != 1:
+            size = _('1 reply, ${attachments} attachments')
+        elif messages != 1 and attachments == 1:
+            size = _('${messages} replies, 1 attachment')
+        else: 
+            size = _('${messages} replies, ${attachments} attachments')
+  
+        size.mapping = {'messages': `messages`, 'attachments': `attachments`}
+
+        return size

Added: messageboard/trunk/step06/messageboard.py
===================================================================
--- messageboard/trunk/step06/messageboard.py	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/messageboard.py	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,47 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Message Board Implementation
+
+An implementation of the Message Board using BTreeContainers as base.
+
+$Id$
+"""
+from zope.interface import implements
+from zope.app.container.btree import BTreeContainer
+
+from book.messageboard.interfaces import IMessageBoard
+
+class MessageBoard(BTreeContainer):
+    """A very simple implementation of a message board using B-Tree Containers
+
+    Make sure that the ``MessageBoard`` implements the ``IMessageBoard``
+    interface:
+
+    >>> from zope.interface.verify import verifyClass
+    >>> verifyClass(IMessageBoard, MessageBoard)
+    True
+    
+    Here is an example of changing the description of the board:
+
+    >>> board = MessageBoard()
+    >>> board.description
+    u''
+    >>> board.description = u'Message Board Description'
+    >>> board.description
+    u'Message Board Description'
+    """
+    implements(IMessageBoard)
+
+    # See book.messageboard.interfaces.IMessageBoard
+    description = u''

Added: messageboard/trunk/step06/security.zcml
===================================================================
--- messageboard/trunk/step06/security.zcml	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/security.zcml	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,50 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope">
+
+  <role
+      id="book.messageboard.User"
+      title="Message Board User"
+      description="Users that actually use the Message Board."
+      />
+  <grant
+      permission="book.messageboard.View"
+      role="book.messageboard.User"
+      />
+  <grant
+      permission="book.messageboard.Add"
+      role="book.messageboard.User"
+      />
+
+  <role
+      id="book.messageboard.Editor"
+      title="Message Board Editor"
+      description="The Editor can edit and delete Messages."
+      />
+  <grant
+      permission="book.messageboard.Edit"
+      role="book.messageboard.Editor"
+      />
+  <grant
+      permission="book.messageboard.Delete"
+      role="book.messageboard.Editor"
+      />
+
+
+  <grant
+      permission="book.messageboard.View"
+      role="zope.Manager"
+      />
+  <grant
+      permission="book.messageboard.Add"
+      role="zope.Manager"
+      />
+  <grant
+      permission="book.messageboard.Edit"
+      role="zope.Manager"
+      />
+  <grant
+      permission="book.messageboard.Delete"
+      role="zope.Manager"
+      /> 
+
+</configure>

Added: messageboard/trunk/step06/tests/__init__.py
===================================================================
--- messageboard/trunk/step06/tests/__init__.py	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/tests/__init__.py	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1 @@
+# Make it a Python package

Added: messageboard/trunk/step06/tests/test_fields.py
===================================================================
--- messageboard/trunk/step06/tests/test_fields.py	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/tests/test_fields.py	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,64 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Message Board Tests
+
+$Id: test_fields.py,v 1.1 2003/06/10 14:40:45 srichter Exp $
+"""
+import unittest
+from zope.schema.tests.test_strfield import TextTest
+
+from book.messageboard.fields import HTML, ForbiddenTags
+
+class HTMLTest(TextTest):
+
+    _Field_Factory = HTML
+
+    def test_AllowedTagsHTMLValidate(self):
+        html = self._Field_Factory(allowed_tags=('h1','pre'))
+        html.validate(u'<h1>Blah</h1>') 
+        html.validate(u'<pre>Blah</pre>') 
+        html.validate(u'<h1><pre>Blah</pre></h1>') 
+        html.validate(u'<h1 style="..."><pre>Blah</pre></h1>') 
+        html.validate(u'<h1 style="..."><pre f="">Blah</pre></h1>') 
+
+        self.assertRaises(ForbiddenTags, html.validate,
+                          u'<h2>Foo</h2>')
+        self.assertRaises(ForbiddenTags, html.validate,
+                          u'<h2><pre>Foo</pre></h2>')
+        self.assertRaises(ForbiddenTags, html.validate,
+                          u'<h2 attr="blah">Foo</h2>')
+
+
+    def test_ForbiddenTagsHTMLValidate(self):
+        html = self._Field_Factory(forbidden_tags=('h2','pre'))
+        html.validate(u'<h1>Blah</h1>') 
+        html.validate(u'<h1 style="...">Blah</h1>') 
+        html.validate(u'<h1 style="..."><div>Blah</div></h1>') 
+        html.validate(u'<h1 style="..."><div f="">Blah</div></h1>') 
+
+        self.assertRaises(ForbiddenTags, html.validate,
+                          u'<h2>Foo</h2>')
+        self.assertRaises(ForbiddenTags, html.validate,
+                          u'<h2><div>Foo</div></h2>')
+        self.assertRaises(ForbiddenTags, html.validate,
+                          u'<h2 attr="blah">Foo</h2>')
+
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(HTMLTest),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Added: messageboard/trunk/step06/tests/test_message.py
===================================================================
--- messageboard/trunk/step06/tests/test_message.py	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/tests/test_message.py	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,40 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Message Board Tests
+
+$Id: test_message.py,v 1.2 2003/08/20 17:07:46 srichter Exp $
+"""
+import unittest
+from zope.testing.doctestunit import DocTestSuite
+
+from zope.app.container.tests.test_icontainer import TestSampleContainer
+from zope.app.tests.placelesssetup import setUp, tearDown 
+
+from book.messageboard.message import Message
+
+
+class Test(TestSampleContainer):
+
+    def makeTestObject(self):
+        return Message()
+
+def test_suite():
+    return unittest.TestSuite((
+        DocTestSuite('book.messageboard.message',
+                     setUp=setUp, tearDown=tearDown),
+        unittest.makeSuite(Test),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Added: messageboard/trunk/step06/tests/test_messageboard.py
===================================================================
--- messageboard/trunk/step06/tests/test_messageboard.py	2004-05-18 18:45:05 UTC (rev 24809)
+++ messageboard/trunk/step06/tests/test_messageboard.py	2004-05-18 19:12:11 UTC (rev 24810)
@@ -0,0 +1,38 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Message Board Tests
+
+$Id: test_messageboard.py,v 1.2 2003/08/20 17:07:46 srichter Exp $
+"""
+import unittest
+from zope.testing.doctestunit import DocTestSuite
+
+from zope.app.container.tests.test_icontainer import TestSampleContainer
+
+from book.messageboard.messageboard import MessageBoard
+
+
+class Test(TestSampleContainer):
+
+    def makeTestObject(self):
+        return MessageBoard()
+
+def test_suite():
+    return unittest.TestSuite((
+        DocTestSuite('book.messageboard.messageboard'),
+        unittest.makeSuite(Test),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')




More information about the Zope-CVS mailing list