[Checkins] SVN: CMF/trunk/C - CMFDefault.Document and CMFDefault.NewsItem: Added a format choice for

Jens Vagelpohl jens at dataflake.org
Tue Jun 12 13:12:11 EDT 2007


Log message for revision 76639:
  - CMFDefault.Document and CMFDefault.NewsItem: Added a format choice for
    ReStructuredText.
    (http://www.zope.org/Collectors/CMF/485)
  

Changed:
  U   CMF/trunk/CHANGES.txt
  U   CMF/trunk/CMFCore/tests/base/content.py
  U   CMF/trunk/CMFDefault/Document.py
  U   CMF/trunk/CMFDefault/browser/document.py
  U   CMF/trunk/CMFDefault/skins/zpt_content/document_edit_template.pt
  U   CMF/trunk/CMFDefault/skins/zpt_content/newsitem_edit_template.pt
  U   CMF/trunk/CMFDefault/tests/test_Document.py
  U   CMF/trunk/CMFDefault/tests/test_NewsItem.py

-=-
Modified: CMF/trunk/CHANGES.txt
===================================================================
--- CMF/trunk/CHANGES.txt	2007-06-12 16:09:28 UTC (rev 76638)
+++ CMF/trunk/CHANGES.txt	2007-06-12 17:12:10 UTC (rev 76639)
@@ -2,6 +2,10 @@
 
   New Features
 
+    - CMFDefault.Document and CMFDefault.NewsItem: Added a format choice for
+      ReStructuredText.
+      (http://www.zope.org/Collectors/CMF/485)
+
     - CMFCalendar.CalendarTool: Added a new method getNextEvent to grab the
       next event relative to a given point in time.
       (http://www.zope.org/Collectors/CMF/462)

Modified: CMF/trunk/CMFCore/tests/base/content.py
===================================================================
--- CMF/trunk/CMFCore/tests/base/content.py	2007-06-12 16:09:28 UTC (rev 76638)
+++ CMF/trunk/CMFCore/tests/base/content.py	2007-06-12 17:12:10 UTC (rev 76639)
@@ -172,3 +172,56 @@
 
 http://www.zope.org
 """
+
+BASIC_ReST = '''\
+Title: My Document
+Description: A document by me
+Contributors: foo at bar.com; baz at bam.net; no at yes.maybe
+Subject: content management, zope
+Keywords: unit tests; , framework
+
+==================
+This is the header
+==================
+
+Body body body body body body body body.
+
+* A list item
+
+* And another thing...
+'''
+
+ReST_WITH_HTML = """\
+Sometimes people do interesting things
+======================================
+
+Sometimes people do interesting things like have examples
+of HTML inside their ReStructured text document.  We should
+be detecting that this is indeed a restructured text document
+and **NOT** an HTML document::
+
+  <html>
+  <head><title>Hello World</title></head>
+  <body><p>Hello world, I am Bruce.</p></body>
+  </html>
+
+All in favor say pi!
+"""
+
+
+ReST_NO_HEADERS = """\
+============
+Title Phrase
+============
+This is a "plain" ReST file, with no headers.  Saving with
+it shouldn't overwrite any metadata.
+"""
+
+ReST_NO_HEADERS_BUT_COLON = """\
+======================
+Plain ReST:  No magic!
+======================
+
+This is a "plain" ReST file, with no headers.  Saving with
+it shouldn't overwrite any metadata.
+"""

Modified: CMF/trunk/CMFDefault/Document.py
===================================================================
--- CMF/trunk/CMFDefault/Document.py	2007-06-12 16:09:28 UTC (rev 76638)
+++ CMF/trunk/CMFDefault/Document.py	2007-06-12 17:12:10 UTC (rev 76639)
@@ -19,10 +19,17 @@
 from AccessControl import ClassSecurityInfo
 from AccessControl import getSecurityManager
 from Acquisition import aq_base
+from App.config import getConfiguration
 from DocumentTemplate.DT_Util import html_quote
 from Globals import DTMLFile
 from Globals import InitializeClass
+try:
+    from reStructuredText import HTML as ReST
+    REST_AVAILABLE = True
+except ImportError:
+    REST_AVAILABLE = False
 from StructuredText.StructuredText import HTML
+   
 from zope.component.factory import Factory
 from zope.interface import implements
 
@@ -66,6 +73,8 @@
     _size = 0
 
     _stx_level = 1                      # Structured text level
+    _rest_level = getConfiguration().rest_header_level # comes from zope.conf
+    rest_available = REST_AVAILABLE
 
     _last_safety_belt_editor = ''
     _last_safety_belt = ''
@@ -102,10 +111,14 @@
         self._size = len(text)
 
         text_format = self.text_format
+        if not text_format:
+            text_format = self.text_format
         if text_format == 'html':
             self.cooked_text = text
         elif text_format == 'plain':
             self.cooked_text = html_quote(text).replace('\n', '<br />')
+        elif text_format == 'restructured-text':
+            self.cooked_text = ReST(text, initial_header_level=self._rest_level)
         else:
             self.cooked_text = HTML(text, level=self._stx_level, header=0)
 
@@ -266,22 +279,34 @@
     #
 
     security.declareProtected(View, 'CookedBody')
-    def CookedBody(self, stx_level=None, setlevel=0):
+    def CookedBody(self, stx_level=None, setlevel=0, rest_level=None):
         """ Get the "cooked" (ready for presentation) form of the text.
 
         The prepared basic rendering of an object.  For Documents, this
         means pre-rendered structured text, or what was between the
         <BODY> tags of HTML.
 
-        If the format is html, and 'stx_level' is not passed in or is the
-        same as the object's current settings, return the cached cooked
-        text.  Otherwise, recook.  If we recook and 'setlevel' is true,
-        then set the recooked text and stx_level on the object.
+        If the format is html, and 'stx_level' or 'rest_level' are not 
+        passed in or is the same as the object's current settings, return 
+        the cached cooked text.  Otherwise, recook.  If we recook and 
+        'setlevel' is true, then set the recooked text and stx_level or 
+        rest_level on the object.
         """
-        if (self.text_format == 'html' or self.text_format == 'plain'
+        if (
+            (self.text_format == 'html' or self.text_format == 'plain'
             or (stx_level is None)
-            or (stx_level == self._stx_level)):
+            or (stx_level == self._stx_level))
+            and 
+            ((rest_level is None) 
+            or (rest_level == self._rest_level))
+            ):
             return self.cooked_text
+        elif rest_level is not None:
+            cooked = ReST(self.text, initial_header_level=rest_level)
+            if setlevel:
+                self._rest_level = rest_level
+                self.cooked_text = cooked
+            return cooked
         else:
             cooked = HTML(self.text, level=stx_level, header=0)
             if setlevel:
@@ -321,14 +346,17 @@
         """
         value = str(format)
         old_value = self.text_format
+        text_formats = ('structured-text', 'plain', 'restructured-text')
 
         if value == 'text/html' or value == 'html':
             self.text_format = 'html'
         elif value == 'text/plain':
-            if self.text_format not in ('structured-text', 'plain'):
+            if self.text_format not in text_formats:
                 self.text_format = 'structured-text'
         elif value == 'plain':
             self.text_format = 'plain'
+        elif value == 'restructured-text':
+            self.text_format = 'restructured-text'
         else:
             self.text_format = 'structured-text'
 

Modified: CMF/trunk/CMFDefault/browser/document.py
===================================================================
--- CMF/trunk/CMFDefault/browser/document.py	2007-06-12 16:09:28 UTC (rev 76638)
+++ CMF/trunk/CMFDefault/browser/document.py	2007-06-12 17:12:10 UTC (rev 76639)
@@ -38,11 +38,19 @@
 from utils import memoize
 from utils import ViewBase
 
+from Products.CMFDefault.Document import REST_AVAILABLE
+
 available_text_formats = (
         (u'structured-text', 'structured-text', _(u'structured-text')),
         (u'plain', 'plain', _(u'plain text')),
         (u'html', 'html', _(u'html')))
 
+if REST_AVAILABLE:
+    available_text_formats +=  ( ( u'restructured-text'
+                                 , 'restructured-text'
+                                 , _(u'restructured-text')
+                                 ), )
+
 TextFormatVocabularyFactory = StaticVocabulary(available_text_formats)
 
 

Modified: CMF/trunk/CMFDefault/skins/zpt_content/document_edit_template.pt
===================================================================
--- CMF/trunk/CMFDefault/skins/zpt_content/document_edit_template.pt	2007-06-12 16:09:28 UTC (rev 76638)
+++ CMF/trunk/CMFDefault/skins/zpt_content/document_edit_template.pt	2007-06-12 17:12:10 UTC (rev 76639)
@@ -23,6 +23,11 @@
  <tr>
   <th i18n:translate="">Format</th>
   <td>
+    <tal:condition tal:condition="context/rest_available">
+     <input type="radio" name="text_format" value="restructured-text" id="cb_rest"
+         tal:attributes="checked python: options['text_format']=='restructured-text'" />
+     <label for="cb_rest" i18n:translate="">ReStructured-text</label>
+     </tal:condition>
    <input type="radio" name="text_format" value="structured-text" id="cb_stx"
       tal:attributes="checked python: options['text_format']=='structured-text'" />
    <label for="cb_stx" i18n:translate="">structured-text</label>

Modified: CMF/trunk/CMFDefault/skins/zpt_content/newsitem_edit_template.pt
===================================================================
--- CMF/trunk/CMFDefault/skins/zpt_content/newsitem_edit_template.pt	2007-06-12 16:09:28 UTC (rev 76638)
+++ CMF/trunk/CMFDefault/skins/zpt_content/newsitem_edit_template.pt	2007-06-12 17:12:10 UTC (rev 76639)
@@ -18,6 +18,11 @@
  <tr>
   <th i18n:translate="">Format</th>
   <td>
+   <tal:condition tal:condition="context/rest_available">
+    <input type="radio" name="text_format" value="restructured-text" id="cb_rest"
+           tal:attributes="checked python: options['text_format']=='restructured-text'" />
+     <label for="cb_rest" i18n:translate="">ReStructured-text</label>
+   </tal:condition>
    <input type="radio" name="text_format" value="structured-text" id="cb_stx"
       tal:attributes="checked python: options['text_format']=='structured-text'" />
    <label for="cb_stx" i18n:translate="">structured-text</label>

Modified: CMF/trunk/CMFDefault/tests/test_Document.py
===================================================================
--- CMF/trunk/CMFDefault/tests/test_Document.py	2007-06-12 16:09:28 UTC (rev 76638)
+++ CMF/trunk/CMFDefault/tests/test_Document.py	2007-06-12 17:12:10 UTC (rev 76639)
@@ -30,11 +30,15 @@
 
 from Products.CMFCore.testing import ConformsToContent
 from Products.CMFCore.tests.base.content import BASIC_HTML
+from Products.CMFCore.tests.base.content import BASIC_ReST
 from Products.CMFCore.tests.base.content import BASIC_STRUCTUREDTEXT
 from Products.CMFCore.tests.base.content import DOCTYPE
 from Products.CMFCore.tests.base.content import ENTITY_IN_TITLE
 from Products.CMFCore.tests.base.content import FAUX_HTML_LEADING_TEXT
 from Products.CMFCore.tests.base.content import HTML_TEMPLATE
+from Products.CMFCore.tests.base.content import ReST_NO_HEADERS
+from Products.CMFCore.tests.base.content import ReST_NO_HEADERS_BUT_COLON
+from Products.CMFCore.tests.base.content import ReST_WITH_HTML
 from Products.CMFCore.tests.base.content import SIMPLE_STRUCTUREDTEXT
 from Products.CMFCore.tests.base.content import SIMPLE_XHTML
 from Products.CMFCore.tests.base.content import STX_NO_HEADERS
@@ -258,6 +262,75 @@
         self.failIf( d.CookedBody().find('<h1') >= 0 )
         self.failUnless( d.CookedBody().find('<h2') >= 0 )
 
+    def test_ReStructuredText(self):
+        self.REQUEST['BODY'] = BASIC_ReST
+        d = self._makeOne('foo')
+        d.PUT(self.REQUEST, self.RESPONSE)
+        d.edit(text_format='restructured-text', text=BASIC_ReST)
+
+        self.failUnless( hasattr(d, 'cooked_text') )
+        self.assertEqual( d.Format(), 'text/plain' )
+        self.assertEqual( d.Title(), 'My Document' )
+        self.assertEqual( d.Description(), 'A document by me' )
+        self.assertEqual( len(d.Contributors()), 3 )
+
+        # CookedBody() for consistency
+        self.failUnless( d.CookedBody().find('<p>') >= 0 )
+
+        #  override default
+        self.failUnless( d.CookedBody(rest_level=0).find('<h1') >= 0 )
+
+        # we should check for the list
+        self.failUnless( d.CookedBody().find('<ul') >= 0 )
+
+        # Make sure extra HTML is NOT found
+        self.failUnless( d.cooked_text.find('<title>') < 0 )
+        self.failUnless( d.cooked_text.find('<body>') < 0 )
+
+        # test subject/keyword headers
+        subj = list(d.Subject())
+        self.assertEqual( len(subj), 4 )
+        subj.sort()
+        self.assertEqual( subj, [ 'content management'
+                                , 'framework'
+                                , 'unit tests'
+                                , 'zope'
+                                ] )
+
+    def test_EditReStructuredTextWithHTML(self):
+        d = self._makeOne('foo')
+        d.edit(text_format='restructured-text', text=ReST_WITH_HTML)
+
+        self.assertEqual(d.Format(), 'text/plain')
+        self.assertEqual(d.get_size(), len(ReST_WITH_HTML))
+
+    def test_ReST_Levels(self):
+        d = self._makeOne('foo')
+        d.edit(text_format='restructured-text', text=BASIC_ReST)
+        d.CookedBody(rest_level=0, setlevel=True)
+        self.assertEqual( d._rest_level, 0)
+
+        ct = d.CookedBody()
+
+        # ReST always augments the level by one
+        self.failUnless( d.CookedBody().find('<h1') >= 0 )
+        self.assertEqual( d._rest_level, 0)
+
+        ct = d.CookedBody(rest_level=1)
+        self.failIf( ct.find('<h1') >= 0 )
+        self.failUnless( ct.find('<h2') >= 0 )
+        self.assertEqual( d._rest_level, 0 )
+
+        ct = d.CookedBody(rest_level=1, setlevel=1)
+        self.failIf( ct.find('<h1') >= 0 )
+        self.failUnless( ct.find('<h2') >= 0 )
+        self.assertEqual( d._rest_level, 1 )
+
+        ct = d.CookedBody()
+        self.assertEqual( d._rest_level, 1 )
+        self.failIf( d.CookedBody().find('<h1') >= 0 )
+        self.failUnless( d.CookedBody().find('<h2') >= 0 )
+
     def test_Init(self):
         self.REQUEST['BODY']=BASIC_STRUCTUREDTEXT
         d = self._makeOne('foo')
@@ -312,6 +385,29 @@
         self.failUnless( 'plain' in d.Subject() )
         self.failUnless( 'STX' in d.Subject() )
 
+    def test_ReST_NoHeaders( self ):
+        self.REQUEST['BODY'] = ReST_NO_HEADERS
+        d = self._makeOne('foo')
+        d.editMetadata( title="Plain ReST"
+                       , description="Look, Ma, no headers!"
+                       , subject=( "plain", "ReST" )
+                       )
+        self.assertEqual( d.Format(), 'text/html' )
+        self.assertEqual( d.Title(), 'Plain ReST' )
+        self.assertEqual( d.Description(), 'Look, Ma, no headers!' )
+        self.assertEqual( len( d.Subject() ), 2 )
+        self.failUnless( 'plain' in d.Subject() )
+        self.failUnless( 'ReST' in d.Subject() )
+
+        d.PUT(self.REQUEST, self.RESPONSE)
+
+        self.assertEqual( d.Format(), 'text/plain' )
+        self.assertEqual( d.Title(), 'Plain ReST' )
+        self.assertEqual( d.Description(), 'Look, Ma, no headers!' )
+        self.assertEqual( len( d.Subject() ), 2 )
+        self.failUnless( 'plain' in d.Subject() )
+        self.failUnless( 'ReST' in d.Subject() )
+
     def test_STX_NoHeaders_but_colon( self ):
         d = self._makeOne('foo')
         d.editMetadata( title="Plain STX"
@@ -322,6 +418,16 @@
         d.edit(text_format='structured-text', text=STX_NO_HEADERS_BUT_COLON)
         self.assertEqual( d.EditableBody(), STX_NO_HEADERS_BUT_COLON )
 
+    def test_ReST_NoHeaders_but_colon( self ):
+        d = self._makeOne('foo')
+        d.editMetadata( title="Plain ReST"
+                       , description="Look, Ma, no headers!"
+                       , subject=( "plain", "ST" )
+                       )
+
+        d.edit(text_format='restructured-text', text=ReST_NO_HEADERS_BUT_COLON)
+        self.assertEqual( d.EditableBody(), ReST_NO_HEADERS_BUT_COLON )
+
     def test_ZMI_edit( self ):
         d = self._makeOne('foo')
         d.editMetadata( title="Plain STX"
@@ -347,6 +453,12 @@
         d.setFormat( d.Format() )
         self.assertEqual( d.text_format, 'structured-text' )
 
+        d.setFormat('restructured-text')
+        self.assertEqual( d.text_format, 'restructured-text' )
+        self.assertEqual( d.Format(), 'text/plain' )
+        d.setFormat( d.Format() )
+        self.assertEqual( d.text_format, 'restructured-text' )
+
         d.setFormat('html')
         self.assertEqual( d.text_format, 'html' )
         self.assertEqual( d.Format(), 'text/html' )
@@ -517,6 +629,22 @@
         self.assertEqual( d.Format(), 'text/plain' )
         self.assertEqual( r.status, 204 )
 
+    def test_PutReStructuredTextWithHTML(self):
+        self.REQUEST['BODY'] = ReST_WITH_HTML
+        d = self._makeOne('foo')
+        r = d.PUT(self.REQUEST, self.RESPONSE)
+
+        self.assertEqual( d.Format(), 'text/plain' )
+        self.assertEqual( r.status, 204 )
+
+    def test_PutReStructuredText(self):
+        self.REQUEST['BODY'] = BASIC_ReST
+        d = self._makeOne('foo')
+        r = d.PUT(self.REQUEST, self.RESPONSE)
+
+        self.assertEqual( d.Format(), 'text/plain' )
+        self.assertEqual( r.status, 204 )
+
     def test_PutHtmlWithDoctype(self):
         html = '%s\n\n  \n   %s' % (DOCTYPE, BASIC_HTML)
         self.REQUEST['BODY'] = html

Modified: CMF/trunk/CMFDefault/tests/test_NewsItem.py
===================================================================
--- CMF/trunk/CMFDefault/tests/test_NewsItem.py	2007-06-12 16:09:28 UTC (rev 76638)
+++ CMF/trunk/CMFDefault/tests/test_NewsItem.py	2007-06-12 17:12:10 UTC (rev 76639)
@@ -22,6 +22,7 @@
 
 from Products.CMFCore.testing import ConformsToContent
 from Products.CMFCore.tests.base.content import BASIC_HTML
+from Products.CMFCore.tests.base.content import BASIC_ReST
 from Products.CMFCore.tests.base.content import BASIC_STRUCTUREDTEXT
 from Products.CMFCore.tests.base.content import DOCTYPE
 from Products.CMFCore.tests.base.content import ENTITY_IN_TITLE
@@ -68,6 +69,15 @@
         self.assertEqual( d.text_format, 'structured-text' )
         self.assertEqual( d.text, '' )
 
+    def test_Empty_ReST(self):
+        d = self._makeOne('foo', text_format='restructured-text')
+
+        self.assertEqual( d.Title(), '' )
+        self.assertEqual( d.Description(), '' )
+        self.assertEqual( d.Format(), 'text/plain' )
+        self.assertEqual( d.text_format, 'restructured-text' )
+        self.assertEqual( d.text, '' )
+
     def test_Init_with_stx( self ):
         d = self._makeOne('foo', text_format='structured-text',
                           title='Foodoc')
@@ -78,6 +88,16 @@
         self.assertEqual( d.text_format, 'structured-text' )
         self.assertEqual( d.text, '' )
 
+    def test_Init_with_ReST( self ):
+        d = self._makeOne('foo', text_format='restructured-text',
+                          title='Foodoc')
+
+        self.assertEqual( d.Title(), 'Foodoc' )
+        self.assertEqual( d.Description(), '' )
+        self.assertEqual( d.Format(), 'text/plain' )
+        self.assertEqual( d.text_format, 'restructured-text' )
+        self.assertEqual( d.text, '' )
+
     def test_default_format( self ):
         d = self._makeOne('foo', text='')
 



More information about the Checkins mailing list