web/views/basecomponents.py
changeset 0 b97547f5f1fa
child 29 7d14f1eadded
equal deleted inserted replaced
-1:000000000000 0:b97547f5f1fa
       
     1 """Bases HTML components:
       
     2 
       
     3 * the rql input form
       
     4 * the logged user link
       
     5 * the workflow history section for workflowable objects
       
     6 
       
     7 :organization: Logilab
       
     8 :copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     9 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
    10 """
       
    11 __docformat__ = "restructuredtext en"
       
    12 
       
    13 from rql import parse
       
    14 
       
    15 from cubicweb import Unauthorized
       
    16 from cubicweb.common.uilib import html_escape, toggle_action
       
    17 from cubicweb.common.selectors import yes_selector
       
    18 from cubicweb.schema import display_name
       
    19 from cubicweb.common.selectors import (chainfirst, multitype_selector,
       
    20                                     req_form_params_selector)
       
    21 
       
    22 from cubicweb.web.htmlwidgets import MenuWidget, PopupBoxMenu, BoxSeparator, BoxLink
       
    23 from cubicweb.web.component import (SingletonVComponent, EntityVComponent, 
       
    24                                  RelatedObjectsVComponent)
       
    25 
       
    26 _ = unicode
       
    27 
       
    28 
       
    29 class RQLInputForm(SingletonVComponent):
       
    30     """build the rql input form, usually displayed in the header"""
       
    31     id = 'rqlinput'
       
    32     visible = False
       
    33        
       
    34     def call(self, view=None):
       
    35         if hasattr(view, 'filter_box_context_info'):
       
    36             rset = view.filter_box_context_info()[0]
       
    37         else:
       
    38             rset = self.rset
       
    39         # display multilines query as one line
       
    40         rql = rset is not None and rset.printable_rql(encoded=False) or self.req.form.get('rql', '')
       
    41         rql = rql.replace(u"\n", u" ")
       
    42         req = self.req
       
    43         self.w(u'''<div id="rqlinput" class="%s">
       
    44           <form action="%s">
       
    45 <fieldset>
       
    46 <input type="text" id="rql" name="rql" value="%s"  title="%s" tabindex="%s" accesskey="q" class="searchField" />
       
    47 <input type="submit" value="%s" class="searchButton" tabindex="%s" />
       
    48 </fieldset>
       
    49 ''' % (not self.propval('visible') and 'hidden' or '', 
       
    50        self.build_url('view'), html_escape(rql), req._('full text or RQL query'), req.next_tabindex(),
       
    51        req._('search'), req.next_tabindex()))
       
    52         if self.req.search_state[0] != 'normal':
       
    53             self.w(u'<input type="hidden" name="__mode" value="%s"/>'
       
    54                    % ':'.join(req.search_state[1]))
       
    55         self.w(u'</form></div>')
       
    56 
       
    57 
       
    58 class ApplLogo(SingletonVComponent):
       
    59     """build the application logo, usually displayed in the header"""
       
    60     id = 'logo'
       
    61     site_wide = True # don't want user to hide this component using an eproperty
       
    62     def call(self):
       
    63         self.w(u'<a href="%s"><img class="logo" src="%s" alt="logo"/></a>'
       
    64                % (self.req.base_url(), self.req.external_resource('LOGO')))
       
    65 
       
    66 
       
    67 class ApplHelp(SingletonVComponent):
       
    68     """build the help button, usually displayed in the header"""
       
    69     id = 'help'
       
    70     def call(self):
       
    71         self.w(u'<a href="%s" class="help" title="%s">&nbsp;</a>'
       
    72                % (self.build_url(_restpath='doc/main'),
       
    73                   self.req._(u'help'),))
       
    74 
       
    75 
       
    76 class UserLink(SingletonVComponent):
       
    77     """if the user is the anonymous user, build a link to login
       
    78     else a link to the connected user object with a loggout link
       
    79     """
       
    80     id = 'loggeduserlink'
       
    81     site_wide = True # don't want user to hide this component using an eproperty
       
    82 
       
    83     def call(self):
       
    84         if not self.req.cnx.anonymous_connection:
       
    85             # display useractions and siteactions
       
    86             actions = self.vreg.possible_actions(self.req, self.rset)
       
    87             box = MenuWidget('', 'userActionsBox', _class='', islist=False)
       
    88             menu = PopupBoxMenu(self.req.user.login, isitem=False)
       
    89             box.append(menu)
       
    90             for action in actions.get('useractions', ()):
       
    91                 menu.append(BoxLink(action.url(), self.req._(action.title),
       
    92                                     action.html_class()))
       
    93             if actions.get('useractions') and actions.get('siteactions'):
       
    94                 menu.append(BoxSeparator())
       
    95             for action in actions.get('siteactions', ()):
       
    96                 menu.append(BoxLink(action.url(), self.req._(action.title),
       
    97                                     action.html_class()))
       
    98             box.render(w=self.w)
       
    99         else:
       
   100             self.anon_user_link()
       
   101             
       
   102     def anon_user_link(self):
       
   103         if self.config['auth-mode'] == 'cookie':
       
   104             self.w(self.req._('anonymous'))
       
   105             self.w(u'''&nbsp;[<a class="logout" href="javascript:toggleVisibility('popupLoginBox'); document.login_form.__login.focus() ">%s</a>]'''
       
   106                    % (self.req._('i18n_login_popup')))
       
   107         else:
       
   108             self.w(self.req._('anonymous'))
       
   109             self.w(u'&nbsp;[<a class="logout" href="%s">%s</a>]'
       
   110                    % (self.build_url('login'), self.req._('login')))
       
   111 
       
   112 
       
   113 class ApplicationMessage(SingletonVComponent):
       
   114     """display application's messages given using the __message parameter
       
   115     into a special div section
       
   116     """
       
   117     __selectors__ = yes_selector,
       
   118     id = 'applmessages'
       
   119     site_wide = True # don't want user to hide this component using an eproperty
       
   120 
       
   121     def call(self):
       
   122         msgs = [msg for msg in (self.req.get_shared_data('sources_error', pop=True),
       
   123                                 self.req.message) if msg]
       
   124         self.w(u'<div id="appMsg" onclick="%s" class="%s">\n' %
       
   125                (toggle_action('appMsg'), (msgs and ' ' or 'hidden')))
       
   126         for msg in msgs:
       
   127             self.w(u'<div class="message" id="%s">%s</div>' % (
       
   128                 self.div_id(), msg))
       
   129         self.w(u'</div>')
       
   130 
       
   131 
       
   132 class WFHistoryVComponent(EntityVComponent):
       
   133     """display the workflow history for entities supporting it"""
       
   134     id = 'wfhistory'
       
   135     accepts = ('Any',)
       
   136     context = 'navcontentbottom'
       
   137     rtype = 'wf_info_for'
       
   138     target = 'subject'
       
   139     title = _('Workflow history')
       
   140 
       
   141     def call(self, view=None):
       
   142         _ = self.req._
       
   143         eid = self.rset[0][0]
       
   144         sel = 'Any FS,TS,WF,D'
       
   145         rql = ' ORDERBY D DESC WHERE WF wf_info_for X,'\
       
   146               'WF from_state FS, WF to_state TS, WF comment C,'\
       
   147               'WF creation_date D'
       
   148         if self.vreg.schema.eschema('EUser').has_perm(self.req, 'read'):
       
   149             sel += ',U,C'
       
   150             rql += ', WF owned_by U?'
       
   151             displaycols = range(5)
       
   152             headers = (_('from_state'), _('to_state'), _('comment'), _('date'),
       
   153                        _('EUser'))            
       
   154         else:
       
   155             sel += ',C'
       
   156             displaycols = range(4)
       
   157             headers = (_('from_state'), _('to_state'), _('comment'), _('date'))
       
   158         rql = '%s %s, X eid %%(x)s' % (sel, rql)
       
   159         try:
       
   160             rset = self.req.execute(rql, {'x': eid}, 'x')
       
   161         except Unauthorized:
       
   162             return
       
   163         if rset:
       
   164             self.wview('table', rset, title=_(self.title), displayactions=False,
       
   165                        displaycols=displaycols, headers=headers)
       
   166 
       
   167 
       
   168 class ApplicationName(SingletonVComponent):
       
   169     """display the application name"""
       
   170     id = 'appliname'
       
   171 
       
   172     def call(self):
       
   173         self.w(u'<span id="appliName"><a href="%s">%s</a></span>' % (self.req.base_url(),
       
   174                                                          self.req.property_value('ui.site-title')))
       
   175         
       
   176 
       
   177 class SeeAlsoVComponent(RelatedObjectsVComponent):
       
   178     """display any entity's see also"""
       
   179     id = 'seealso'
       
   180     context = 'navcontentbottom'
       
   181     rtype = 'see_also'
       
   182     target = 'object'
       
   183     order = 40
       
   184     # register msg not generated since no entity use see_also in cubicweb itself
       
   185     title = _('contentnavigation_seealso')
       
   186     help = _('contentnavigation_seealso_description')
       
   187 
       
   188     
       
   189 class EtypeRestrictionComponent(SingletonVComponent):
       
   190     """displays the list of entity types contained in the resultset
       
   191     to be able to filter accordingly.
       
   192     """
       
   193     id = 'etypenavigation'
       
   194     __select__ = classmethod(chainfirst(multitype_selector, req_form_params_selector))
       
   195     form_params = ('__restrtype', '__restrtypes', '__restrrql')
       
   196     visible = False # disabled by default
       
   197     
       
   198     def call(self):
       
   199         _ = self.req._
       
   200         self.w(u'<div id="etyperestriction">')
       
   201         restrtype = self.req.form.get('__restrtype')
       
   202         restrtypes = self.req.form.get('__restrtypes', '').split(',')
       
   203         restrrql = self.req.form.get('__restrrql')
       
   204         if not restrrql:
       
   205             rqlst = self.rset.syntax_tree()
       
   206             restrrql = rqlst.as_string(self.req.encoding, self.rset.args)
       
   207             restrtypes = self.rset.column_types(0)
       
   208         else:
       
   209             rqlst = parse(restrrql)
       
   210         html = []
       
   211         on_etype = False
       
   212         etypes = sorted((display_name(self.req, etype).capitalize(), etype)
       
   213                         for etype in restrtypes)
       
   214         for elabel, etype in etypes:
       
   215             if etype == restrtype:
       
   216                 html.append(u'<span class="selected">%s</span>' % elabel)
       
   217                 on_etype = True
       
   218             else:
       
   219                 rqlst.save_state()
       
   220                 for select in rqlst.children:
       
   221                     select.add_type_restriction(select.selection[0], etype)
       
   222                 newrql = rqlst.as_string(self.req.encoding, self.rset.args)
       
   223                 url = self.build_url(rql=newrql, __restrrql=restrrql,
       
   224                                      __restrtype=etype, __restrtypes=','.join(restrtypes))
       
   225                 html.append(u'<span><a href="%s">%s</a></span>' % (
       
   226                         html_escape(url), elabel))
       
   227                 rqlst.recover()
       
   228         if on_etype:
       
   229             url = self.build_url(rql=restrrql)
       
   230             html.insert(0, u'<span><a href="%s">%s</a></span>' % (
       
   231                     url, _('Any')))
       
   232         else:
       
   233             html.insert(0, u'<span class="selected">%s</span>' % _('Any'))
       
   234         self.w(u'&nbsp;|&nbsp;'.join(html))
       
   235         self.w(u'</div>')
       
   236