cubicweb/view.py
changeset 12780 8caa109dfe94
parent 12567 26744ad37953
child 12831 b1ef9690f357
equal deleted inserted replaced
12779:44147dab9d27 12780:8caa109dfe94
    18 """abstract views and templates classes for CubicWeb web client"""
    18 """abstract views and templates classes for CubicWeb web client"""
    19 
    19 
    20 
    20 
    21 from cubicweb import _
    21 from cubicweb import _
    22 
    22 
       
    23 import re
    23 from io import BytesIO
    24 from io import BytesIO
    24 from warnings import warn
    25 from warnings import warn
    25 from functools import partial
    26 from functools import partial
       
    27 from inspect import getframeinfo, stack
    26 
    28 
    27 from logilab.common.registry import yes
    29 from logilab.common.registry import yes
    28 from logilab.mtconverter import xml_escape
    30 from logilab.mtconverter import xml_escape
    29 
    31 
    30 from rql import nodes
    32 from rql import nodes
    43 TRANSITIONAL_DOCTYPE_NOEXT = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n'
    45 TRANSITIONAL_DOCTYPE_NOEXT = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n'
    44 TRANSITIONAL_DOCTYPE = TRANSITIONAL_DOCTYPE_NOEXT # bw compat
    46 TRANSITIONAL_DOCTYPE = TRANSITIONAL_DOCTYPE_NOEXT # bw compat
    45 
    47 
    46 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'
    48 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'
    47 STRICT_DOCTYPE = STRICT_DOCTYPE_NOEXT # bw compat
    49 STRICT_DOCTYPE = STRICT_DOCTYPE_NOEXT # bw compat
       
    50 
       
    51 
       
    52 def inject_html_generating_call_on_w():
       
    53     View.debug_html_rendering = True
       
    54 
    48 
    55 
    49 # base view object ############################################################
    56 # base view object ############################################################
    50 
    57 
    51 class View(AppObject):
    58 class View(AppObject):
    52     """This class is an abstraction of a view class, used as a base class for
    59     """This class is an abstraction of a view class, used as a base class for
    84     # content_type = 'application/xhtml+xml' # text/xhtml'
    91     # content_type = 'application/xhtml+xml' # text/xhtml'
    85     binary = False
    92     binary = False
    86     add_to_breadcrumbs = True
    93     add_to_breadcrumbs = True
    87     category = 'view'
    94     category = 'view'
    88     paginable = True
    95     paginable = True
       
    96     debug_html_rendering = False
    89 
    97 
    90     def __init__(self, req=None, rset=None, **kwargs):
    98     def __init__(self, req=None, rset=None, **kwargs):
    91         super(View, self).__init__(req, rset=rset, **kwargs)
    99         super(View, self).__init__(req, rset=rset, **kwargs)
    92         self.w = None
   100         self._w = None
    93 
   101 
    94     @property
   102     @property
    95     def content_type(self):
   103     def content_type(self):
    96         return self._cw.html_content_type()
   104         return self._cw.html_content_type()
    97 
   105 
       
   106     def w(self, text):
       
   107         if self._w is None:
       
   108             raise Exception('Error: call to %s.w before it has been set' % self)
       
   109 
       
   110         if self.debug_html_rendering:
       
   111             caller = getframeinfo(stack()[1][0])
       
   112 
       
   113             if isinstance(text, str) and re.match(r"^ *<[a-zA-Z0-9]+( .*>|>)", text):
       
   114                 to_add = 'cubicweb-generated-by="%s.%s" cubicweb-from-source="%s:%s"' % (
       
   115                     self.__module__, self.__class__.__name__,
       
   116                     caller.filename, caller.lineno,
       
   117                 )
       
   118 
       
   119                 before_space, beginning_of_html = text.split("<", 1)
       
   120 
       
   121                 # when it's a tag without attribues like "<b>"
       
   122                 if re.match(r"^ *<[a-zA-Z0-9]+>", text):
       
   123                     tag_name, rest = beginning_of_html.split(">", 1)
       
   124                     rest = ">" + rest
       
   125                 else:
       
   126                     tag_name, rest = beginning_of_html.split(" ", 1)
       
   127                     rest = " " + rest
       
   128 
       
   129                 text = "%(before_space)s<%(tag_name)s %(to_add)s%(rest)s" % {
       
   130                     "before_space": before_space,
       
   131                     "tag_name": tag_name,
       
   132                     "to_add": to_add,
       
   133                     "rest": rest,
       
   134                 }
       
   135 
       
   136         return self._w(text)
       
   137 
    98     def set_stream(self, w=None):
   138     def set_stream(self, w=None):
    99         if self.w is not None:
   139         if self._w is not None:
   100             return
   140             return
   101         if w is None:
   141         if w is None:
   102             if self.binary:
   142             if self.binary:
   103                 self._stream = stream = BytesIO()
   143                 self._stream = stream = BytesIO()
   104             else:
   144             else:
   105                 self._stream = stream = UStringIO()
   145                 self._stream = stream = UStringIO()
   106             w = stream.write
   146             w = stream.write
   107         else:
   147         else:
   108             stream = None
   148             stream = None
   109         self.w = w
   149         self._w = w
   110         return stream
   150         return stream
   111 
   151 
   112     # main view interface #####################################################
   152     # main view interface #####################################################
   113 
   153 
   114     def render(self, w=None, **context):
   154     def render(self, w=None, **context):
   236     # view utilities ##########################################################
   276     # view utilities ##########################################################
   237 
   277 
   238     def wview(self, __vid, rset=None, __fallback_vid=None, **kwargs):
   278     def wview(self, __vid, rset=None, __fallback_vid=None, **kwargs):
   239         """shortcut to self.view method automatically passing self.w as argument
   279         """shortcut to self.view method automatically passing self.w as argument
   240         """
   280         """
   241         self._cw.view(__vid, rset, __fallback_vid, w=self.w, **kwargs)
   281         self._cw.view(__vid, rset, __fallback_vid, w=self._w, **kwargs)
   242 
   282 
   243     def whead(self, data):
   283     def whead(self, data):
   244         self._cw.html_headers.write(data)
   284         self._cw.html_headers.write(data)
   245 
   285 
   246     def wdata(self, data):
   286     def wdata(self, data):
   464     """
   504     """
   465 
   505 
   466     doctype = '<!DOCTYPE html>'
   506     doctype = '<!DOCTYPE html>'
   467 
   507 
   468     def set_stream(self, w=None):
   508     def set_stream(self, w=None):
   469         if self.w is not None:
   509         if self._w is not None:
   470             return
   510             return
   471         if w is None:
   511         if w is None:
   472             if self.binary:
   512             if self.binary:
   473                 self._stream = stream = BytesIO()
   513                 self._stream = stream = BytesIO()
   474             else:
   514             else:
   475                 self._stream = stream = HTMLStream(self._cw)
   515                 self._stream = stream = HTMLStream(self._cw)
   476             w = stream.write
   516             w = stream.write
   477         else:
   517         else:
   478             stream = None
   518             stream = None
   479         self.w = w
   519         self._w = w
   480         return stream
   520         return stream
   481 
   521 
   482     def write_doctype(self, xmldecl=True):
   522     def write_doctype(self, xmldecl=True):
   483         assert isinstance(self._stream, HTMLStream)
   523         assert isinstance(self._stream, HTMLStream)
   484         self._stream.doctype = self.doctype
   524         self._stream.doctype = self.doctype