cubicweb/view.py
changeset 12780 8caa109dfe94
parent 12567 26744ad37953
child 12831 b1ef9690f357
--- a/cubicweb/view.py	Thu Nov 28 11:48:03 2019 +0100
+++ b/cubicweb/view.py	Wed Jul 24 16:11:22 2019 +0200
@@ -20,9 +20,11 @@
 
 from cubicweb import _
 
+import re
 from io import BytesIO
 from warnings import warn
 from functools import partial
+from inspect import getframeinfo, stack
 
 from logilab.common.registry import yes
 from logilab.mtconverter import xml_escape
@@ -46,6 +48,11 @@
 STRICT_DOCTYPE_NOEXT = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n'
 STRICT_DOCTYPE = STRICT_DOCTYPE_NOEXT # bw compat
 
+
+def inject_html_generating_call_on_w():
+    View.debug_html_rendering = True
+
+
 # base view object ############################################################
 
 class View(AppObject):
@@ -86,17 +93,50 @@
     add_to_breadcrumbs = True
     category = 'view'
     paginable = True
+    debug_html_rendering = False
 
     def __init__(self, req=None, rset=None, **kwargs):
         super(View, self).__init__(req, rset=rset, **kwargs)
-        self.w = None
+        self._w = None
 
     @property
     def content_type(self):
         return self._cw.html_content_type()
 
+    def w(self, text):
+        if self._w is None:
+            raise Exception('Error: call to %s.w before it has been set' % self)
+
+        if self.debug_html_rendering:
+            caller = getframeinfo(stack()[1][0])
+
+            if isinstance(text, str) and re.match(r"^ *<[a-zA-Z0-9]+( .*>|>)", text):
+                to_add = 'cubicweb-generated-by="%s.%s" cubicweb-from-source="%s:%s"' % (
+                    self.__module__, self.__class__.__name__,
+                    caller.filename, caller.lineno,
+                )
+
+                before_space, beginning_of_html = text.split("<", 1)
+
+                # when it's a tag without attribues like "<b>"
+                if re.match(r"^ *<[a-zA-Z0-9]+>", text):
+                    tag_name, rest = beginning_of_html.split(">", 1)
+                    rest = ">" + rest
+                else:
+                    tag_name, rest = beginning_of_html.split(" ", 1)
+                    rest = " " + rest
+
+                text = "%(before_space)s<%(tag_name)s %(to_add)s%(rest)s" % {
+                    "before_space": before_space,
+                    "tag_name": tag_name,
+                    "to_add": to_add,
+                    "rest": rest,
+                }
+
+        return self._w(text)
+
     def set_stream(self, w=None):
-        if self.w is not None:
+        if self._w is not None:
             return
         if w is None:
             if self.binary:
@@ -106,7 +146,7 @@
             w = stream.write
         else:
             stream = None
-        self.w = w
+        self._w = w
         return stream
 
     # main view interface #####################################################
@@ -238,7 +278,7 @@
     def wview(self, __vid, rset=None, __fallback_vid=None, **kwargs):
         """shortcut to self.view method automatically passing self.w as argument
         """
-        self._cw.view(__vid, rset, __fallback_vid, w=self.w, **kwargs)
+        self._cw.view(__vid, rset, __fallback_vid, w=self._w, **kwargs)
 
     def whead(self, data):
         self._cw.html_headers.write(data)
@@ -466,7 +506,7 @@
     doctype = '<!DOCTYPE html>'
 
     def set_stream(self, w=None):
-        if self.w is not None:
+        if self._w is not None:
             return
         if w is None:
             if self.binary:
@@ -476,7 +516,7 @@
             w = stream.write
         else:
             stream = None
-        self.w = w
+        self._w = w
         return stream
 
     def write_doctype(self, xmldecl=True):