[CMF-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 CMF-checkins
mailing list