web/views/basetemplates.py
changeset 1808 aa09e20dd8c0
parent 1776 4be367276874
child 1851 c707419980f8
equal deleted inserted replaced
1693:49075f57cf2c 1808:aa09e20dd8c0
     8 __docformat__ = "restructuredtext en"
     8 __docformat__ = "restructuredtext en"
     9 
     9 
    10 
    10 
    11 from logilab.mtconverter import html_escape
    11 from logilab.mtconverter import html_escape
    12 
    12 
    13 from cubicweb import NoSelectableObject, ObjectNotFound
    13 from cubicweb.vregistry import objectify_selector
    14 from cubicweb.common.view import Template, MainTemplate,  NOINDEX, NOFOLLOW
    14 from cubicweb.selectors import match_kwargs
    15 from cubicweb.common.utils import make_uid
    15 from cubicweb.view import View, MainTemplate,  NOINDEX, NOFOLLOW, STRICT_DOCTYPE
    16 from cubicweb.common.utils import UStringIO
    16 from cubicweb.utils import make_uid, UStringIO
    17 
       
    18 from cubicweb.web.views.baseviews import vid_from_rset
       
    19 
    17 
    20 # main templates ##############################################################
    18 # main templates ##############################################################
    21 
    19 
    22 
       
    23 class LogInOutTemplate(MainTemplate):
    20 class LogInOutTemplate(MainTemplate):
    24     
    21 
    25     def call(self):
    22     def call(self):
    26         self.set_request_content_type()
    23         self.set_request_content_type()
    27         w = self.w
    24         w = self.w
    28         self.write_doctype()
    25         self.write_doctype()
    29         lang = self.req.lang
       
    30         self.template_header('text/html', self.req._('login_action'))
    26         self.template_header('text/html', self.req._('login_action'))
    31         w(u'<body>\n')
    27         w(u'<body>\n')
    32         self.content(w)
    28         self.content(w)
    33         w(u'</body>')
    29         w(u'</body>')
    34 
    30 
    39         w(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n'
    35         w(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n'
    40           % (content_type, self.req.encoding))
    36           % (content_type, self.req.encoding))
    41         w(NOINDEX)
    37         w(NOINDEX)
    42         w(NOFOLLOW)
    38         w(NOFOLLOW)
    43         w(u'\n'.join(additional_headers) + u'\n')
    39         w(u'\n'.join(additional_headers) + u'\n')
    44         self.template('htmlheader', rset=self.rset)
    40         self.wview('htmlheader', rset=self.rset)
    45         w(u'<title>%s</title>\n' % html_escape(page_title))
    41         w(u'<title>%s</title>\n' % html_escape(page_title))
    46         
    42 
    47 
    43 
    48 class LogInTemplate(LogInOutTemplate):
    44 class LogInTemplate(LogInOutTemplate):
    49     id = 'login'
    45     id = 'login'
    50     title = 'log in'
    46     title = 'log in'
    51 
    47 
    52     def content(self, w):
    48     def content(self, w):
    53         self.template('logform', rset=self.rset, id='loginBox', klass='')
    49         self.wview('logform', rset=self.rset, id='loginBox', klass='')
    54         
    50 
    55 
    51 
    56 class LoggedOutTemplate(LogInOutTemplate):
    52 class LoggedOutTemplate(LogInOutTemplate):
    57     id = 'loggedout'
    53     id = 'loggedout'
    58     title = 'logged out'
    54     title = 'logged out'
    59 
    55 
    65             indexurl = self.build_url('view', vid='index', __message=msg)
    61             indexurl = self.build_url('view', vid='index', __message=msg)
    66             w(u'<p><a href="%s">%s</a><p>' % (
    62             w(u'<p><a href="%s">%s</a><p>' % (
    67                 html_escape(indexurl),
    63                 html_escape(indexurl),
    68                 self.req._('go back to the index page')))
    64                 self.req._('go back to the index page')))
    69 
    65 
    70         
    66 @objectify_selector
       
    67 def templatable_view(cls, req, rset, *args, **kwargs):
       
    68     view = kwargs.pop('view', None)
       
    69     if view is None:
       
    70         return 1
       
    71     if view.binary:
       
    72         return 0
       
    73     if req.form.has_key('__notemplate'):
       
    74         return 0
       
    75     return view.templatable
       
    76 
       
    77 
       
    78 class NonTemplatableViewTemplate(MainTemplate):
       
    79     """main template for any non templatable views (xml, binaries, etc.)"""
       
    80     id = 'main-template'
       
    81     __select__ = ~templatable_view()
       
    82 
       
    83     def call(self, view):
       
    84         view.set_request_content_type()
       
    85         view.set_stream()
       
    86         xhtml_wrap = (self.req.form.has_key('__notemplate') and view.templatable
       
    87                       and view.content_type == self.req.html_content_type())
       
    88         if xhtml_wrap:
       
    89             view.w(u'<?xml version="1.0"?>\n' + STRICT_DOCTYPE)
       
    90             view.w(u'<div xmlns="http://www.w3.org/1999/xhtml" xmlns:cubicweb="http://www.logilab.org/2008/cubicweb">')
       
    91         # have to replace our unicode stream using view's binary stream
       
    92         view.render()
       
    93         if xhtml_wrap:
       
    94             view.w(u'</div>')
       
    95         self._stream = view._stream
       
    96 
       
    97 
    71 class TheMainTemplate(MainTemplate):
    98 class TheMainTemplate(MainTemplate):
    72     """default main template :
    99     """default main template :
    73     
   100 
    74     - call header / footer templates
   101     - call header / footer templates
    75     - build result set
       
    76     - guess and call an appropriate view through the view manager
       
    77     """
   102     """
    78     id = 'main'
   103     id = 'main-template'
    79 
   104     __select__ = templatable_view()
    80     def _select_view_and_rset(self):
   105 
    81         req = self.req
   106     def call(self, view):
    82         if self.rset is None and not hasattr(req, '_rql_processed'):
   107         self.set_request_content_type()
    83             req._rql_processed = True
   108         self.template_header(self.content_type, view)
    84             rset = self.process_rql(req.form.get('rql'))
   109         w = self.w
    85         else:
   110         w(u'<div id="pageContent">\n')
    86             rset = self.rset
   111         vtitle = self.req.form.get('vtitle')
    87         # handle special "method" param when necessary
   112         if vtitle:
    88         # XXX this should probably not be in the template (controller ?), however:
   113             w(u'<h1 class="vtitle">%s</h1>\n' % html_escape(vtitle))
    89         #     * we need to have the displayed rset
   114         # display entity type restriction component
    90         #     * we don't want to handle it in each view
   115         etypefilter = self.vreg.select_component('etypenavigation',
    91         if rset and rset.rowcount == 1 and '__method' in req.form:
   116                                                  self.req, self.rset)
    92             entity = rset.get_entity(0, 0)
   117         if etypefilter and etypefilter.propval('visible'):
    93             try:
   118             etypefilter.render(w=w)
    94                 method = getattr(entity, req.form.pop('__method'))
   119         self.nav_html = UStringIO()
    95                 method()
   120         if view and view.need_navigation:
    96             except Exception, ex:
   121             view.paginate(w=self.nav_html.write)
    97                 self.exception('while handling __method')
   122         w(_(self.nav_html.getvalue()))
    98                 req.set_message(req._("error while handling __method: %s") % req._(ex))
   123         w(u'<div id="contentmain">\n')
    99         vid = req.form.get('vid') or vid_from_rset(req, rset, self.schema)
   124         view.render(w=w)
   100         try:
   125         w(u'</div>\n') # close id=contentmain
   101             view = self.vreg.select_view(vid, req, rset)
   126         w(_(self.nav_html.getvalue()))
   102         except ObjectNotFound:
   127         w(u'</div>\n') # closes id=pageContent
   103             self.warning("the view %s could not be found", vid)
   128         self.template_footer(view)
   104             req.set_message(req._("The view %s could not be found") % vid)
       
   105             vid = vid_from_rset(req, rset, self.schema)
       
   106             view = self.vreg.select_view(vid, req, rset)
       
   107         except NoSelectableObject:
       
   108             if rset:
       
   109                 req.set_message(req._("The view %s can not be applied to this query") % vid)
       
   110             else:
       
   111                 req.set_message(req._("You have no access to this view or it's not applyable to current data"))
       
   112             self.warning("the view %s can not be applied to this query", vid)
       
   113             vid = vid_from_rset(req, rset, self.schema)
       
   114             view = self.vreg.select_view(vid, req, rset)
       
   115         return view, rset
       
   116     
       
   117     def call(self):
       
   118         view, rset = self._select_view_and_rset()
       
   119         req = self.req
       
   120         # update breadcrumps **before** validating cache, unless the view
       
   121         # specifies explicitly it should not be added to breadcrumb or the
       
   122         # view is a binary view
       
   123         if view.add_to_breadcrumbs and not view.binary:
       
   124             req.update_breadcrumbs()
       
   125         view.set_http_cache_headers()
       
   126         req.validate_cache()
       
   127         with_templates = not view.binary and view.templatable and \
       
   128                          not req.form.has_key('__notemplate')
       
   129         if not with_templates:
       
   130             view.set_request_content_type()
       
   131             self.set_stream(templatable=False)
       
   132         else:
       
   133             self.set_request_content_type()
       
   134             content_type = self.content_type
       
   135             self.template_header(content_type, view)
       
   136         if view.binary:
       
   137             # have to replace our unicode stream using view's binary stream
       
   138             view.dispatch()
       
   139             assert self._stream, 'duh, template used as a sub-view ?? (%s)' % self._stream
       
   140             self._stream = view._stream
       
   141         else:
       
   142             view.dispatch(w=self.w)
       
   143         if with_templates:
       
   144             self.template_footer(view)
       
   145 
       
   146             
       
   147     def process_rql(self, rql):
       
   148         """execute rql if specified"""
       
   149         if rql:
       
   150             self.ensure_ro_rql(rql)
       
   151             if not isinstance(rql, unicode):
       
   152                 rql = unicode(rql, self.req.encoding)
       
   153             pp = self.vreg.select_component('magicsearch', self.req)
       
   154             self.rset = pp.process_query(rql, self.req)
       
   155             return self.rset
       
   156         return None
       
   157 
   129 
   158     def template_header(self, content_type, view=None, page_title='', additional_headers=()):
   130     def template_header(self, content_type, view=None, page_title='', additional_headers=()):
   159         page_title = page_title or view.page_title()
   131         page_title = page_title or view.page_title()
   160         additional_headers = additional_headers or view.html_headers()
   132         additional_headers = additional_headers or view.html_headers()
   161         self.template_html_header(content_type, page_title, additional_headers)
   133         self.template_html_header(content_type, page_title, additional_headers)
   162         self.template_body_header(view)
   134         self.template_body_header(view)
   163         # display entity type restriction component
   135 
   164         etypefilter = self.vreg.select_component('etypenavigation',
       
   165                                                  self.req, self.rset)
       
   166         if etypefilter and etypefilter.propval('visible'):
       
   167             etypefilter.dispatch(w=self.w)
       
   168         self.nav_html = UStringIO()
       
   169         self.pagination(self.req, self.rset, self.nav_html.write,
       
   170                         not (view and view.need_navigation))
       
   171         self.w(_(self.nav_html.getvalue()))
       
   172         self.w(u'<div id="contentmain">\n')
       
   173     
       
   174     def template_html_header(self, content_type, page_title, additional_headers=()):
   136     def template_html_header(self, content_type, page_title, additional_headers=()):
   175         w = self.whead
   137         w = self.whead
   176         lang = self.req.lang
   138         lang = self.req.lang
   177         self.write_doctype()
   139         self.write_doctype()
   178         w(u'<base href="%s" />' % html_escape(self.req.base_url()))
   140         w(u'<base href="%s" />' % html_escape(self.req.base_url()))
   179         w(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n'
   141         w(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n'
   180           % (content_type, self.req.encoding))
   142           % (content_type, self.req.encoding))
   181         w(u'\n'.join(additional_headers) + u'\n')
   143         w(u'\n'.join(additional_headers) + u'\n')
   182         self.template('htmlheader', rset=self.rset)
   144         self.wview('htmlheader', rset=self.rset)
   183         if page_title:
   145         if page_title:
   184             w(u'<title>%s</title>\n' % html_escape(page_title))
   146             w(u'<title>%s</title>\n' % html_escape(page_title))
   185 
   147 
   186     def template_body_header(self, view):
   148     def template_body_header(self, view):
   187         w = self.w
   149         w = self.w
   188         w(u'<body>\n')
   150         w(u'<body>\n')
   189         self.template('header', rset=self.rset, view=view)
   151         self.wview('header', rset=self.rset, view=view)
   190         w(u'<div id="page"><table width="100%" border="0" id="mainLayout"><tr>\n')
   152         w(u'<div id="page"><table width="100%" border="0" id="mainLayout"><tr>\n')
   191         self.nav_column(view, 'left')
   153         self.nav_column(view, 'left')
   192         w(u'<td id="contentcol">\n')
   154         w(u'<td id="contentcol">\n')
   193         rqlcomp = self.vreg.select_component('rqlinput', self.req, self.rset)
   155         rqlcomp = self.vreg.select_component('rqlinput', self.req, self.rset)
   194         if rqlcomp:
   156         if rqlcomp:
   195             rqlcomp.dispatch(w=self.w, view=view)
   157             rqlcomp.render(w=self.w, view=view)
   196         msgcomp = self.vreg.select_component('applmessages', self.req, self.rset)
   158         msgcomp = self.vreg.select_component('applmessages', self.req, self.rset)
   197         if msgcomp:
   159         if msgcomp:
   198             msgcomp.dispatch(w=self.w)
   160             msgcomp.render(w=self.w)
   199         self.content_header(view)
   161         self.content_header(view)
   200         w(u'<div id="pageContent">\n')
   162 
   201         vtitle = self.req.form.get('vtitle')
       
   202         if vtitle:
       
   203             w(u'<h1 class="vtitle">%s</h1>\n' % html_escape(vtitle))
       
   204             
       
   205     def template_footer(self, view=None):
   163     def template_footer(self, view=None):
   206         self.w(u'</div>\n') # close id=contentmain
       
   207         self.w(_(self.nav_html.getvalue()))
       
   208         self.w(u'</div>\n') # closes id=pageContent
       
   209         self.content_footer(view)
   164         self.content_footer(view)
   210         self.w(u'</td>\n')
   165         self.w(u'</td>\n')
   211         self.nav_column(view, 'right')
   166         self.nav_column(view, 'right')
   212         self.w(u'</tr></table></div>\n')
   167         self.w(u'</tr></table></div>\n')
   213         self.template('footer', rset=self.rset)
   168         self.wview('footer', rset=self.rset)
   214         self.w(u'</body>')
   169         self.w(u'</body>')
   215 
   170 
   216     def nav_column(self, view, context):
   171     def nav_column(self, view, context):
   217         boxes = list(self.vreg.possible_vobjects('boxes', self.req, self.rset,
   172         boxes = list(self.vreg.possible_vobjects('boxes', self.req, self.rset,
   218                                                  view=view, context=context))
   173                                                  view=view, context=context))
   219         if boxes:
   174         if boxes:
   220             self.w(u'<td class="navcol"><div class="navboxes">\n')
   175             self.w(u'<td class="navcol"><div class="navboxes">\n')
   221             for box in boxes:
   176             for box in boxes:
   222                 box.dispatch(w=self.w, view=view)
   177                 box.render(w=self.w, view=view)
   223             self.w(u'</div></td>\n')
   178             self.w(u'</div></td>\n')
   224 
   179 
   225     def content_header(self, view=None):
   180     def content_header(self, view=None):
   226         """by default, display informal messages in content header"""
   181         """by default, display informal messages in content header"""
   227         self.template('contentheader', rset=self.rset, view=view)
   182         self.wview('contentheader', rset=self.rset, view=view)
   228             
   183 
   229     def content_footer(self, view=None):
   184     def content_footer(self, view=None):
   230         self.template('contentfooter', rset=self.rset, view=view)
   185         self.wview('contentfooter', rset=self.rset, view=view)
   231 
   186 
   232 
   187 
   233 class ErrorTemplate(TheMainTemplate):
   188 class ErrorTemplate(TheMainTemplate):
   234     """fallback template if an internal error occured during displaying the
   189     """fallback template if an internal error occured during displaying the
   235     main template. This template may be called for authentication error,
   190     main template. This template may be called for authentication error,
   236     which means that req.cnx and req.user may not be set.
   191     which means that req.cnx and req.user may not be set.
   237     """
   192     """
   238     id = 'error'
   193     id = 'error-template'
   239     
   194 
   240     def call(self):
   195     def call(self):
   241         """display an unexpected error"""
   196         """display an unexpected error"""
   242         self.set_request_content_type()
   197         self.set_request_content_type()
   243         self.req.reset_headers()
   198         self.req.reset_headers()
   244         view = self.vreg.select_view('error', self.req, self.rset)
   199         view = self.vreg.select_view('error', self.req, self.rset)
   245         self.template_header(self.content_type, view, self.req._('an error occured'),
   200         self.template_header(self.content_type, view, self.req._('an error occured'),
   246                              [NOINDEX, NOFOLLOW])
   201                              [NOINDEX, NOFOLLOW])
   247         view.dispatch(w=self.w)
   202         view.render(w=self.w)
   248         self.template_footer(view)
   203         self.template_footer(view)
   249     
   204 
   250     def template_header(self, content_type, view=None, page_title='', additional_headers=()):
   205     def template_header(self, content_type, view=None, page_title='', additional_headers=()):
   251         w = self.whead
   206         w = self.whead
   252         lang = self.req.lang
   207         lang = self.req.lang
   253         self.write_doctype()
   208         self.write_doctype()
   254         w(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n'
   209         w(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n'
   255           % (content_type, self.req.encoding))
   210           % (content_type, self.req.encoding))
   256         w(u'\n'.join(additional_headers))
   211         w(u'\n'.join(additional_headers))
   257         self.template('htmlheader', rset=self.rset)
   212         self.wview('htmlheader', rset=self.rset)
   258         w(u'<title>%s</title>\n' % html_escape(page_title))
   213         w(u'<title>%s</title>\n' % html_escape(page_title))
   259         self.w(u'<body>\n')
   214         self.w(u'<body>\n')
   260 
   215 
   261     def template_footer(self, view=None):
   216     def template_footer(self, view=None):
   262         self.w(u'</body>')
   217         self.w(u'</body>')
   263 
   218 
   264 
   219 
   265 class SimpleMainTemplate(TheMainTemplate):
   220 class SimpleMainTemplate(TheMainTemplate):
   266 
   221 
   267     id = 'main-no-top'
   222     id = 'main-no-top'
   268     
   223 
   269     def template_header(self, content_type, view=None, page_title='', additional_headers=()):
   224     def template_header(self, content_type, view=None, page_title='', additional_headers=()):
   270         page_title = page_title or view.page_title()
   225         page_title = page_title or view.page_title()
   271         additional_headers = additional_headers or view.html_headers()
   226         additional_headers = additional_headers or view.html_headers()
   272         whead = self.whead
   227         whead = self.whead
   273         lang = self.req.lang
   228         lang = self.req.lang
   274         self.write_doctype()
   229         self.write_doctype()
   275         whead(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n'
   230         whead(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n'
   276               % (content_type, self.req.encoding))
   231               % (content_type, self.req.encoding))
   277         whead(u'\n'.join(additional_headers) + u'\n')
   232         whead(u'\n'.join(additional_headers) + u'\n')
   278         self.template('htmlheader', rset=self.rset)
   233         self.wview('htmlheader', rset=self.rset)
   279         w = self.w
   234         w = self.w
   280         w(u'<title>%s</title>\n' % html_escape(page_title))
   235         w(u'<title>%s</title>\n' % html_escape(page_title))
   281         w(u'<body>\n')
   236         w(u'<body>\n')
   282         w(u'<div id="page">')
   237         w(u'<div id="page">')
   283         w(u'<table width="100%" height="100%" border="0"><tr>\n')
   238         w(u'<table width="100%" height="100%" border="0"><tr>\n')
   286         boxes = list(self.vreg.possible_vobjects('boxes', self.req, self.rset,
   241         boxes = list(self.vreg.possible_vobjects('boxes', self.req, self.rset,
   287                                                  view=view, context='left'))
   242                                                  view=view, context='left'))
   288         if boxes:
   243         if boxes:
   289             w(u'<div class="navboxes">\n')
   244             w(u'<div class="navboxes">\n')
   290             for box in boxes:
   245             for box in boxes:
   291                 box.dispatch(w=w)
   246                 box.render(w=w)
   292             self.w(u'</div>\n')
   247             self.w(u'</div>\n')
   293         w(u'</td>')
   248         w(u'</td>')
   294         w(u'<td id="contentcol" rowspan="2">')
   249         w(u'<td id="contentcol" rowspan="2">')
   295         w(u'<div id="pageContent">\n')
   250         w(u'<div id="pageContent">\n')
   296         vtitle = self.req.form.get('vtitle')
   251         vtitle = self.req.form.get('vtitle')
   297         if vtitle:
   252         if vtitle:
   298             w(u'<h1 class="vtitle">%s</h1>' % html_escape(vtitle))
   253             w(u'<h1 class="vtitle">%s</h1>' % html_escape(vtitle))
   299             
   254 
   300     def topleft_header(self):
   255     def topleft_header(self):
   301         self.w(u'<table id="header"><tr>\n')
   256         self.w(u'<table id="header"><tr>\n')
   302         self.w(u'<td>')
   257         self.w(u'<td>')
   303         self.vreg.select_component('logo', self.req, self.rset).dispatch(w=self.w)
   258         self.vreg.select_component('logo', self.req, self.rset).render(w=self.w)
   304         self.w(u'</td>\n')
   259         self.w(u'</td>\n')
   305         self.w(u'</tr></table>\n')
   260         self.w(u'</tr></table>\n')
   306 
   261 
   307 # page parts templates ########################################################
   262 # page parts templates ########################################################
   308 
   263 
   309 class HTMLHeader(Template):
   264 class HTMLHeader(View):
   310     """default html headers"""
   265     """default html headers"""
   311     id = 'htmlheader'
   266     id = 'htmlheader'
   312     
   267 
   313     def call(self, **kwargs):
   268     def call(self, **kwargs):
   314         self.favicon()
   269         self.favicon()
   315         self.stylesheets()
   270         self.stylesheets()
   316         self.javascripts()
   271         self.javascripts()
   317         self.alternates()
   272         self.alternates()
   319 
   274 
   320     def favicon(self):
   275     def favicon(self):
   321         favicon = self.req.external_resource('FAVICON', None)
   276         favicon = self.req.external_resource('FAVICON', None)
   322         if favicon:
   277         if favicon:
   323             self.whead(u'<link rel="shortcut icon" href="%s"/>\n' % favicon)
   278             self.whead(u'<link rel="shortcut icon" href="%s"/>\n' % favicon)
   324             
   279 
   325     def stylesheets(self):
   280     def stylesheets(self):
   326         req = self.req
   281         req = self.req
   327         add_css = req.add_css
   282         add_css = req.add_css
   328         for css in req.external_resource('STYLESHEETS'):
   283         for css in req.external_resource('STYLESHEETS'):
   329             add_css(css, localfile=False)
   284             add_css(css, localfile=False)
   330         for css in req.external_resource('STYLESHEETS_PRINT'):
   285         for css in req.external_resource('STYLESHEETS_PRINT'):
   331             add_css(css, u'print', localfile=False)
   286             add_css(css, u'print', localfile=False)
   332         for css in req.external_resource('IE_STYLESHEETS'):
   287         for css in req.external_resource('IE_STYLESHEETS'):
   333             add_css(css, localfile=False, ieonly=True)
   288             add_css(css, localfile=False, ieonly=True)
   334         
   289 
   335     def javascripts(self):
   290     def javascripts(self):
   336         for jscript in self.req.external_resource('JAVASCRIPTS'):
   291         for jscript in self.req.external_resource('JAVASCRIPTS'):
   337             self.req.add_js(jscript, localfile=False)
   292             self.req.add_js(jscript, localfile=False)
   338             
   293 
   339     def alternates(self):
   294     def alternates(self):
   340         urlgetter = self.vreg.select_component('rss_feed_url', self.req, self.rset)
   295         urlgetter = self.vreg.select_component('rss_feed_url', self.req, self.rset)
   341         if urlgetter is not None:
   296         if urlgetter is not None:
   342             url = urlgetter.feed_url()
   297             url = urlgetter.feed_url()
   343             self.whead(u'<link rel="alternate" type="application/rss+xml" title="RSS feed" href="%s"/>\n'
   298             self.whead(u'<link rel="alternate" type="application/rss+xml" title="RSS feed" href="%s"/>\n'
   345 
   300 
   346     def pageid(self):
   301     def pageid(self):
   347         req = self.req
   302         req = self.req
   348         pid = make_uid(id(req))
   303         pid = make_uid(id(req))
   349         req.pageid = pid
   304         req.pageid = pid
   350         req.html_headers.define_var('pageid', pid);
   305         req.html_headers.define_var('pageid', pid)
   351 
   306 
   352 
   307 
   353 class HTMLPageHeader(Template):
   308 class HTMLPageHeader(View):
   354     """default html page header"""
   309     """default html page header"""
   355     id = 'header'
   310     id = 'header'
   356     
   311 
   357     def call(self, view, **kwargs):
   312     def call(self, view, **kwargs):
   358         self.main_header(view)
   313         self.main_header(view)
   359         self.w(u'''
   314         self.w(u'''
   360   <div id="stateheader">''')
   315   <div id="stateheader">''')
   361         self.state_header()
   316         self.state_header()
   362         self.w(u'''
   317         self.w(u'''
   363   </div>
   318   </div>
   364   ''')
   319   ''')
   365         
   320 
   366     def main_header(self, view):
   321     def main_header(self, view):
   367         """build the top menu with authentification info and the rql box"""
   322         """build the top menu with authentification info and the rql box"""
   368         self.w(u'<table id="header"><tr>\n')
   323         self.w(u'<table id="header"><tr>\n')
   369         self.w(u'<td id="firstcolumn">')
   324         self.w(u'<td id="firstcolumn">')
   370         self.vreg.select_component('logo', self.req, self.rset).dispatch(w=self.w)
   325         self.vreg.select_component('logo', self.req, self.rset).render(w=self.w)
   371         self.w(u'</td>\n')
   326         self.w(u'</td>\n')
   372         # appliname and breadcrumbs
   327         # appliname and breadcrumbs
   373         self.w(u'<td id="headtext">')
   328         self.w(u'<td id="headtext">')
   374         comp = self.vreg.select_component('appliname', self.req, self.rset)
   329         comp = self.vreg.select_component('appliname', self.req, self.rset)
   375         if comp and comp.propval('visible'):
   330         if comp and comp.propval('visible'):
   376             comp.dispatch(w=self.w)
   331             comp.render(w=self.w)
   377         comp = self.vreg.select_component('breadcrumbs', self.req, self.rset, view=view)
   332         comp = self.vreg.select_component('breadcrumbs', self.req, self.rset, view=view)
   378         if comp and comp.propval('visible'):
   333         if comp and comp.propval('visible'):
   379             comp.dispatch(w=self.w, view=view)
   334             comp.render(w=self.w, view=view)
   380         self.w(u'</td>')
   335         self.w(u'</td>')
   381         # logged user and help
   336         # logged user and help
   382         self.w(u'<td>\n')
   337         self.w(u'<td>\n')
   383         comp = self.vreg.select_component('loggeduserlink', self.req, self.rset)
   338         comp = self.vreg.select_component('loggeduserlink', self.req, self.rset)
   384         comp.dispatch(w=self.w)
   339         comp.render(w=self.w)
   385         self.w(u'</td><td>')
   340         self.w(u'</td><td>')
   386         helpcomp = self.vreg.select_component('help', self.req, self.rset)
   341         helpcomp = self.vreg.select_component('help', self.req, self.rset)
   387         if helpcomp: # may not be available if Card is not defined in the schema
   342         if helpcomp: # may not be available if Card is not defined in the schema
   388             helpcomp.dispatch(w=self.w)
   343             helpcomp.render(w=self.w)
   389         self.w(u'</td>')
   344         self.w(u'</td>')
   390         # lastcolumn
   345         # lastcolumn
   391         self.w(u'<td id="lastcolumn">')
   346         self.w(u'<td id="lastcolumn">')
   392         self.w(u'</td>\n')
   347         self.w(u'</td>\n')
   393         self.w(u'</tr></table>\n')
   348         self.w(u'</tr></table>\n')
   394         self.template('logform', rset=self.rset, id='popupLoginBox', klass='hidden',
   349         self.wview('logform', rset=self.rset, id='popupLoginBox', klass='hidden',
   395                       title=False, message=False)
   350                    title=False, message=False)
   396         
   351 
   397     def state_header(self):
   352     def state_header(self):
   398         state = self.req.search_state
   353         state = self.req.search_state
   399         if state[0] == 'normal':
   354         if state[0] == 'normal':
   400             return
   355             return
   401         _ = self.req._
   356         _ = self.req._
   402         value = self.view('oneline', self.req.eid_rset(state[1][1]))
   357         value = self.view('oneline', self.req.eid_rset(state[1][1]))
   403         msg = ' '.join((_("searching for"),
   358         msg = ' '.join((_("searching for"),
   404                         display_name(self.req, state[1][3]),
   359                         display_name(self.req, state[1][3]),
   405                         _("to associate with"), value,
   360                         _("to associate with"), value,
   406                         _("by relation"), '"', 
   361                         _("by relation"), '"',
   407                         display_name(self.req, state[1][2], state[1][0]),
   362                         display_name(self.req, state[1][2], state[1][0]),
   408                         '"'))
   363                         '"'))
   409         return self.w(u'<div class="stateMessage">%s</div>' % msg)
   364         return self.w(u'<div class="stateMessage">%s</div>' % msg)
   410 
   365 
   411 
   366 
   412 
   367 
   413 class HTMLPageFooter(Template):
   368 class HTMLPageFooter(View):
   414     """default html page footer: include logo if any, and close the HTML body
   369     """default html page footer: include logo if any, and close the HTML body
   415     """
   370     """
   416     id = 'footer'
   371     id = 'footer'
   417     
   372 
   418     def call(self, **kwargs):
   373     def call(self, **kwargs):
   419         req = self.req
   374         req = self.req
   420         self.w(u'<div class="footer">')
   375         self.w(u'<div class="footer">')
   421         # XXX Take object from the registry if in there? would be
   376         # XXX Take object from the registry if in there? would be
   422         #     better anyway
   377         #     better anyway
   427                                             req._('about this site')))
   382                                             req._('about this site')))
   428         self.w(u'© 2001-2009 <a href="http://www.logilab.fr">Logilab S.A.</a>')
   383         self.w(u'© 2001-2009 <a href="http://www.logilab.fr">Logilab S.A.</a>')
   429         self.w(u'</div>')
   384         self.w(u'</div>')
   430 
   385 
   431 
   386 
   432 class HTMLContentHeader(Template):
   387 class HTMLContentHeader(View):
   433     """default html page content header:
   388     """default html page content header:
   434     * include message component if selectable for this request
   389     * include message component if selectable for this request
   435     * include selectable content navigation components
   390     * include selectable content navigation components
   436     """
   391     """
   437     id = 'contentheader'
   392     id = 'contentheader'
   438     
   393 
   439     def call(self, view, **kwargs):
   394     def call(self, view, **kwargs):
   440         """by default, display informal messages in content header"""
   395         """by default, display informal messages in content header"""
   441         components = self.vreg.possible_vobjects('contentnavigation',
   396         components = self.vreg.possible_vobjects('contentnavigation',
   442                                                  self.req, self.rset,
   397                                                  self.req, self.rset,
   443                                                  view=view, context='navtop')
   398                                                  view=view, context='navtop')
   444         if components:
   399         if components:
   445             self.w(u'<div id="contentheader">')
   400             self.w(u'<div id="contentheader">')
   446             for comp in components:
   401             for comp in components:
   447                 comp.dispatch(w=self.w, view=view)
   402                 comp.render(w=self.w, view=view)
   448             self.w(u'</div><div class="clear"></div>')
   403             self.w(u'</div><div class="clear"></div>')
   449 
   404 
   450 
   405 
   451 class HTMLContentFooter(Template):
   406 class HTMLContentFooter(View):
   452     """default html page content footer: include selectable content navigation
   407     """default html page content footer: include selectable content navigation
   453     components
   408     components
   454     """
   409     """
   455     id = 'contentfooter'
   410     id = 'contentfooter'
   456     
   411 
   457     def call(self, view, **kwargs):
   412     def call(self, view, **kwargs):
   458         components = self.vreg.possible_vobjects('contentnavigation',
   413         components = self.vreg.possible_vobjects('contentnavigation',
   459                                                  self.req, self.rset,
   414                                                  self.req, self.rset,
   460                                                  view=view, context='navbottom')
   415                                                  view=view, context='navbottom')
   461         if components:
   416         if components:
   462             self.w(u'<div id="contentfooter">')
   417             self.w(u'<div id="contentfooter">')
   463             for comp in components:
   418             for comp in components:
   464                 comp.dispatch(w=self.w, view=view)
   419                 comp.render(w=self.w, view=view)
   465             self.w(u'</div>')
   420             self.w(u'</div>')
   466 
   421 
   467 
   422 
   468 class LogFormTemplate(Template):
   423 class LogFormTemplate(View):
   469     id = 'logform'
   424     id = 'logform'
       
   425     __select__ = match_kwargs('id', 'klass')
       
   426 
   470     title = 'log in'
   427     title = 'log in'
   471 
   428 
   472     def call(self, id, klass, title=True, message=True):
   429     def call(self, id, klass, title=True, message=True):
   473         self.req.add_css('cubicweb.login.css')
   430         self.req.add_css('cubicweb.login.css')
   474         self.w(u'<div id="%s" class="%s">' % (id, klass))
   431         self.w(u'<div id="%s" class="%s">' % (id, klass))
   475         if title:
   432         if title:
   476             self.w(u'<div id="loginTitle">%s</div>'
   433             self.w(u'<div id="loginTitle">%s</div>'
   477                    % self.req.property_value('ui.site-title'))
   434                    % self.req.property_value('ui.site-title'))
   478         self.w(u'<div id="loginContent">\n')        
   435         self.w(u'<div id="loginContent">\n')
   479 
   436 
   480         if message:
   437         if message:
   481             self.display_message()
   438             self.display_message()
   482         if self.config['auth-mode'] == 'http':
   439         if self.config['auth-mode'] == 'http':
   483             # HTTP authentication
   440             # HTTP authentication
   489 
   446 
   490     def display_message(self):
   447     def display_message(self):
   491         message = self.req.message
   448         message = self.req.message
   492         if message:
   449         if message:
   493             self.w(u'<div class="simpleMessage">%s</div>\n' % message)
   450             self.w(u'<div class="simpleMessage">%s</div>\n' % message)
   494                      
   451 
   495     def login_form(self, id):
   452     def login_form(self, id):
   496         _ = self.req._
   453         _ = self.req._
   497         self.w(u'<form method="post" action="%s" id="login_form">\n'
   454         self.w(u'<form method="post" action="%s" id="login_form">\n'
   498                % html_escape(login_form_url(self.config, self.req)))
   455                % html_escape(login_form_url(self.config, self.req)))
   499         self.w(u'<table>\n')
   456         self.w(u'<table>\n')
   500         self.w(u'<tr>\n')
   457         self.w(u'<tr>\n')
   501         self.w(u'<td><label for="__login">%s</label></td>' % _('login'))
   458         msg = (self.config['allow-email-login'] and _('login or email')) or _('login')
       
   459         self.w(u'<td><label for="__login">%s</label></td>' % msg)
   502         self.w(u'<td><input name="__login" id="__login" class="data" type="text" /></td>')
   460         self.w(u'<td><input name="__login" id="__login" class="data" type="text" /></td>')
   503         self.w(u'</tr><tr>\n')
   461         self.w(u'</tr><tr>\n')
   504         self.w(u'<td><label for="__password" >%s</label></td>' % _('password'))
   462         self.w(u'<td><label for="__password" >%s</label></td>' % _('password'))
   505         self.w(u'<td><input name="__password" id="__password" class="data" type="password" /></td>\n')
   463         self.w(u'<td><input name="__password" id="__password" class="data" type="password" /></td>\n')
   506         self.w(u'</tr><tr>\n')
   464         self.w(u'</tr><tr>\n')
   508         self.w(u'</tr>\n')
   466         self.w(u'</tr>\n')
   509         self.w(u'</table>\n')
   467         self.w(u'</table>\n')
   510         self.w(u'</form>\n')
   468         self.w(u'</form>\n')
   511         self.req.html_headers.add_onload('jQuery("#__login:visible").focus()')
   469         self.req.html_headers.add_onload('jQuery("#__login:visible").focus()')
   512 
   470 
   513     
   471 
   514 def login_form_url(config, req):
   472 def login_form_url(config, req):
   515     if req.https:
   473     if req.https:
   516         return req.url()
   474         return req.url()
   517     if config.get('https-url'):
   475     if config.get('https-url'):
   518         return req.url().replace(req.base_url(), config['https-url'])
   476         return req.url().replace(req.base_url(), config['https-url'])
   519     return req.url()
   477     return req.url()
   520 
   478 
       
   479 
       
   480 ## vregistry registration callback ############################################
       
   481 def registration_callback(vreg):
       
   482     vreg.register_all(globals().values(), modname=__name__)