[views] extend HTMLStream API to be able to change doctype / xmldecl
authorAdrien Di Mascio <Adrien.DiMascio@logilab.fr>
Wed, 06 Apr 2011 16:01:19 +0200
changeset 7187 496f51b92154
parent 7184 59d953d8694e
child 7194 79686c864bbf
[views] extend HTMLStream API to be able to change doctype / xmldecl When generating RDFa (for instance), the doctype must be : <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd"> This changeset adds a ``set_doctype()`` method on request objects to allow such changes during page generation.
utils.py
web/request.py
web/test/unittest_views_baseviews.py
--- a/utils.py	Wed Apr 06 10:10:21 2011 +0200
+++ b/utils.py	Wed Apr 06 16:01:19 2011 +0200
@@ -361,17 +361,43 @@
         self.doctype = u''
         # xmldecl and html opening tag
         self.xmldecl = u'<?xml version="1.0" encoding="%s"?>\n' % req.encoding
-        self.htmltag = u'<html xmlns="http://www.w3.org/1999/xhtml" ' \
-                       'xmlns:cubicweb="http://www.logilab.org/2008/cubicweb" ' \
-                       'xml:lang="%s" lang="%s">' % (req.lang, req.lang)
+        self._namespaces = [('xmlns', 'http://www.w3.org/1999/xhtml'),
+                            ('xmlns:cubicweb','http://www.logilab.org/2008/cubicweb')]
+        self._htmlattrs = [('xml:lang', req.lang),
+                           ('lang', req.lang)]
         # keep main_stream's reference on req for easier text/html demoting
         req.main_stream = self
 
+    def add_namespace(self, prefix, uri):
+        self._namespaces.append( (prefix, uri) )
+
+    def set_namespaces(self, namespaces):
+        self._namespaces = namespaces
+
+    def add_htmlattr(self, attrname, attrvalue):
+        self._htmlattrs.append( (attrname, attrvalue) )
+
+    def set_htmlattrs(self, attrs):
+        self._htmlattrs = attrs
+
+    def set_doctype(self, doctype, reset_xmldecl=True):
+        self.doctype = doctype
+        if reset_xmldecl:
+            self.xmldecl = u''
+
     def write(self, data):
         """StringIO interface: this method will be assigned to self.w
         """
         self.body.write(data)
 
+    @property
+    def htmltag(self):
+        attrs = ' '.join('%s="%s"' % (attr, xml_escape(value))
+                         for attr, value in (self._namespaces + self._htmlattrs))
+        if attrs:
+            return '<html %s>' % attrs
+        return '<html>'
+
     def getvalue(self):
         """writes HTML headers, closes </head> tag and writes HTML body"""
         return u'%s\n%s\n%s\n%s\n%s\n</html>' % (self.xmldecl, self.doctype,
@@ -379,7 +405,6 @@
                                                  self.head.getvalue(),
                                                  self.body.getvalue())
 
-
 try:
     # may not be there if cubicweb-web not installed
     if sys.version_info < (2, 6):
--- a/web/request.py	Wed Apr 06 10:10:21 2011 +0200
+++ b/web/request.py	Wed Apr 06 16:01:19 2011 +0200
@@ -756,8 +756,16 @@
         will display '<[' at the beginning of the page
         """
         self.set_content_type('text/html')
-        self.main_stream.doctype = TRANSITIONAL_DOCTYPE_NOEXT
-        self.main_stream.xmldecl = u''
+        self.main_stream.set_doctype(TRANSITIONAL_DOCTYPE_NOEXT)
+
+    def set_doctype(self, doctype, reset_xmldecl=True):
+        """helper method to dynamically change page doctype
+
+        :param doctype: the new doctype, e.g. '<!DOCTYPE html>'
+        :param reset_xmldecl: if True, remove the '<?xml version="1.0"?>'
+                              declaration from the page
+        """
+        self.main_stream.set_doctype(doctype, reset_xmldecl)
 
     # page data management ####################################################
 
--- a/web/test/unittest_views_baseviews.py	Wed Apr 06 10:10:21 2011 +0200
+++ b/web/test/unittest_views_baseviews.py	Wed Apr 06 16:01:19 2011 +0200
@@ -16,11 +16,14 @@
 # You should have received a copy of the GNU Lesser General Public License along
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
 
+from __future__ import with_statement
+
 from logilab.common.testlib import unittest_main
 from logilab.mtconverter import html_unescape
 
 from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.utils import json
+from cubicweb.view import StartupView, TRANSITIONAL_DOCTYPE_NOEXT
 from cubicweb.web.htmlwidgets import TableWidget
 from cubicweb.web.views import vid_from_rset
 
@@ -125,5 +128,47 @@
         self.assertListEqual(got, expected)
 
 
+class HTMLStreamTests(CubicWebTC):
+
+    def test_set_doctype_reset_xmldecl(self):
+        """
+        tests `cubicweb.web.request.CubicWebRequestBase.set_doctype`
+        with xmldecl reset
+        """
+        class MyView(StartupView):
+            __regid__ = 'my-view'
+            def call(self):
+                self._cw.set_doctype('<!DOCTYPE html>')
+
+        with self.temporary_appobjects(MyView):
+            html_source = self.view('my-view').source
+            source_lines = [line.strip() for line in html_source.splitlines(False)
+                            if line.strip()]
+            self.assertListEqual(source_lines[:2],
+                                 ['<!DOCTYPE html>',
+                                  '<html xmlns="http://www.w3.org/1999/xhtml" xmlns:cubicweb="http://www.logilab.org/2008/cubicweb" xml:lang="en" lang="en">'])
+
+    def test_set_doctype_no_reset_xmldecl(self):
+        """
+        tests `cubicweb.web.request.CubicWebRequestBase.set_doctype`
+        with no xmldecl reset
+        """
+        html_doctype = TRANSITIONAL_DOCTYPE_NOEXT.strip()
+        class MyView(StartupView):
+            __regid__ = 'my-view'
+            def call(self):
+                self._cw.set_doctype(html_doctype, reset_xmldecl=False)
+                self._cw.main_stream.set_namespaces([('xmlns', 'http://www.w3.org/1999/xhtml')])
+                self._cw.main_stream.set_htmlattrs([('lang', 'cz')])
+
+        with self.temporary_appobjects(MyView):
+            html_source = self.view('my-view').source
+            source_lines = [line.strip() for line in html_source.splitlines(False)
+                            if line.strip()]
+            self.assertListEqual(source_lines[:3],
+                                 ['<?xml version="1.0" encoding="UTF-8"?>',
+                                  html_doctype,
+                                  '<html xmlns="http://www.w3.org/1999/xhtml" lang="cz">'])
+
 if __name__ == '__main__':
     unittest_main()