web/views/boxes.py
changeset 0 b97547f5f1fa
child 107 4fe4ce7e2544
equal deleted inserted replaced
-1:000000000000 0:b97547f5f1fa
       
     1 """
       
     2 generic boxes for CubicWeb web client:
       
     3 
       
     4 * actions box
       
     5 * possible views box
       
     6 * rss icon
       
     7 
       
     8 additional (disabled by default) boxes
       
     9 * schema box
       
    10 * startup views box
       
    11 
       
    12 :organization: Logilab
       
    13 :copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
    14 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
    15 """
       
    16 __docformat__ = "restructuredtext en"
       
    17 
       
    18 from logilab.mtconverter import html_escape
       
    19 
       
    20 from cubicweb.common.selectors import rset_selector, nfentity_selector
       
    21 from cubicweb.web.htmlwidgets import BoxWidget, BoxMenu, BoxHtml, RawBoxItem
       
    22 from cubicweb.web.box import BoxTemplate, ExtResourcesBoxTemplate
       
    23 
       
    24 _ = unicode
       
    25 
       
    26 
       
    27 class EditBox(BoxTemplate):
       
    28     """
       
    29     box with all actions impacting the entity displayed: edit, copy, delete
       
    30     change state, add related entities
       
    31     """
       
    32     __selectors__ = (rset_selector,) + BoxTemplate.__selectors__
       
    33     id = 'edit_box'
       
    34     title = _('actions')
       
    35     order = 2
       
    36 
       
    37     def call(self, **kwargs):
       
    38         _ = self.req._
       
    39         title = _(self.title)
       
    40         if self.rset:
       
    41             etypes = self.rset.column_types(0)
       
    42             if len(etypes) == 1:
       
    43                 plural = self.rset.rowcount > 1 and 'plural' or ''
       
    44                 etypelabel = display_name(self.req, iter(etypes).next(), plural)
       
    45                 title = u'%s - %s' % (title, etypelabel.lower())
       
    46         box = BoxWidget(title, self.id, _class="greyBoxFrame")
       
    47         # build list of actions
       
    48         actions = self.vreg.possible_actions(self.req, self.rset)
       
    49         add_menu = BoxMenu(_('add')) # 'addrelated' category
       
    50         other_menu = BoxMenu(_('more actions')) # 'moreactions' category
       
    51         searchstate = self.req.search_state[0]
       
    52         for category, menu in (('mainactions', box),
       
    53                                ('addrelated', add_menu),
       
    54                                ('moreactions', other_menu)):
       
    55             for action in actions.get(category, ()):
       
    56                 menu.append(self.box_action(action))
       
    57         if self.rset and self.rset.rowcount == 1 and \
       
    58                not self.schema[self.rset.description[0][0]].is_final() and \
       
    59                searchstate == 'normal':
       
    60             entity = self.rset.get_entity(0, 0)
       
    61             #entity.complete()
       
    62             if add_menu.items:
       
    63                 self.info('explicit actions defined, ignoring potential rtags for %s',
       
    64                           entity.e_schema)
       
    65             else:
       
    66                 # some addrelated actions may be specified but no one is selectable
       
    67                 # in which case we should not fallback to schema_actions. The proper
       
    68                 # way to avoid this is to override add_related_schemas() on the
       
    69                 # entity class to return an empty list
       
    70                 for action in self.schema_actions(entity):
       
    71                     add_menu.append(action)            
       
    72             if 'in_state' in entity.e_schema.subject_relations() and entity.in_state:
       
    73                 state = entity.in_state[0]
       
    74                 transitions = list(state.transitions(entity))
       
    75                 if transitions:
       
    76                     menu_title = u'%s: %s' % (_('state'), state.view('text'))
       
    77                     menu_items = []
       
    78                     for tr in state.transitions(entity):
       
    79                         url = entity.absolute_url(vid='statuschange', treid=tr.eid)
       
    80                         menu_items.append(self.mk_action(_(tr.name), url))
       
    81                     state_menu = BoxMenu(menu_title, menu_items)
       
    82                     box.append(state_menu)
       
    83                 # when there are no possible transition, put state if the menu if
       
    84                 # there are some other actions
       
    85                 elif not box.is_empty():
       
    86                     menu_title = u'<a title="%s">%s: <i>%s</i></a>' % (
       
    87                         _('no possible transition'), _('state'), state.view('text'))
       
    88                     box.append(RawBoxItem(menu_title, 'boxMainactions'))
       
    89         if box.is_empty() and not other_menu.is_empty():
       
    90             box.items = other_menu.items
       
    91             other_menu.items = []
       
    92         self.add_submenu(box, add_menu, _('add'))
       
    93         self.add_submenu(box, other_menu)
       
    94         if not box.is_empty():
       
    95             box.render(self.w)
       
    96 
       
    97     def add_submenu(self, box, submenu, label_prefix=None):
       
    98         if len(submenu.items) == 1:
       
    99             boxlink = submenu.items[0]
       
   100             if label_prefix:
       
   101                 boxlink.label = u'%s %s' % (label_prefix, boxlink.label)
       
   102             box.append(boxlink)
       
   103         elif submenu.items:
       
   104             box.append(submenu)
       
   105         
       
   106     def schema_actions(self, entity):
       
   107         user = self.req.user
       
   108         actions = []
       
   109         _ = self.req._
       
   110         eschema = entity.e_schema
       
   111         for rschema, teschema, x in entity.add_related_schemas():
       
   112             if x == 'subject':
       
   113                 label = 'add %s %s %s %s' % (eschema, rschema, teschema, x)
       
   114                 url = self.linkto_url(entity, rschema, teschema, 'object')
       
   115             else:
       
   116                 label = 'add %s %s %s %s' % (teschema, rschema, eschema, x)
       
   117                 url = self.linkto_url(entity, rschema, teschema, 'subject')
       
   118             actions.append(self.mk_action(_(label), url))
       
   119         return actions
       
   120 
       
   121 
       
   122     def linkto_url(self, entity, rtype, etype, target):
       
   123         
       
   124         return self.build_url(vid='creation', etype=etype,
       
   125                               __linkto='%s:%s:%s' % (rtype, entity.eid, target),
       
   126                               __redirectpath=entity.rest_path(), # should not be url quoted!
       
   127                               __redirectvid=self.req.form.get('vid', ''))
       
   128 
       
   129 
       
   130 class SearchBox(BoxTemplate):
       
   131     """display a box with a simple search form"""
       
   132     id = 'search_box'
       
   133     visible = True # enabled by default
       
   134     title = _('search')
       
   135     order = 0
       
   136     need_resources = 'SEARCH_GO'
       
   137     formdef = u"""<form action="%s">
       
   138 <table id="tsearch"><tr><td>
       
   139 <input id="norql" type="text" accesskey="q" tabindex="%s" title="search text" value="%s" name="rql" />
       
   140 <input type="hidden" name="__fromsearchbox" value="1" />
       
   141 <input type="hidden" name="subvid" value="tsearch" />
       
   142 </td><td>
       
   143 <input tabindex="%s" type="submit" id="rqlboxsubmit" value="" />
       
   144 </td></tr></table>
       
   145 </form>"""
       
   146 
       
   147 
       
   148     def call(self, view=None, **kwargs):
       
   149         req = self.req
       
   150         if req.form.pop('__fromsearchbox', None):
       
   151             rql = req.form.get('rql', '')
       
   152         else:
       
   153             rql = ''
       
   154         form = self.formdef % (req.build_url('view'), req.next_tabindex(),
       
   155                                html_escape(rql), req.next_tabindex())
       
   156         title = u"""<span onclick="javascript: toggleVisibility('rqlinput')">%s</span>""" % req._(self.title)
       
   157         box = BoxWidget(title, self.id, _class="searchBoxFrame", islist=False, escape=False)
       
   158         box.append(BoxHtml(form))
       
   159         box.render(self.w)            
       
   160 
       
   161 
       
   162 # boxes disabled by default ###################################################
       
   163 
       
   164 class PossibleViewsBox(BoxTemplate):
       
   165     """display a box containing links to all possible views"""
       
   166     id = 'possible_views_box'
       
   167     
       
   168     
       
   169     title = _('possible views')
       
   170     order = 10
       
   171     require_groups = ('users', 'managers')
       
   172     visible = False
       
   173 
       
   174     def call(self, **kwargs):
       
   175         box = BoxWidget(self.req._(self.title), self.id)
       
   176         actions = [v for v in self.vreg.possible_views(self.req, self.rset)
       
   177                    if v.category != 'startupview']
       
   178         for category, actions in self.sort_actions(actions):
       
   179             menu = BoxMenu(category)
       
   180             for action in actions:
       
   181                 menu.append(self.box_action(action))
       
   182             box.append(menu)
       
   183         if not box.is_empty():
       
   184             box.render(self.w)
       
   185 
       
   186 
       
   187 class RSSIconBox(ExtResourcesBoxTemplate):
       
   188     """just display the RSS icon on uniform result set"""
       
   189     __selectors__ = ExtResourcesBoxTemplate.__selectors__ + (nfentity_selector,)
       
   190     
       
   191     id = 'rss'
       
   192     order = 999
       
   193     need_resources = 'RSS_LOGO',
       
   194     visible = False
       
   195     
       
   196     def call(self, **kwargs):
       
   197         url = html_escape(self.build_url(rql=self.limited_rql(), vid='rss'))
       
   198         rss = self.req.external_resource('RSS_LOGO')
       
   199         self.w(u'<a href="%s"><img src="%s" border="0" /></a>\n' % (url, rss))
       
   200 
       
   201 
       
   202 ## warning("schemabox ne marche plus pour le moment")
       
   203 ## class SchemaBox(BoxTemplate):
       
   204 ##     """display a box containing link to list of entities by type"""
       
   205 ##     id = 'schema_box'
       
   206 ##     visible = False # disabled by default
       
   207 ##     title = _('entity list')
       
   208 ##     order = 60
       
   209         
       
   210 ##     def call(self, **kwargs):
       
   211 ##         box = BoxWidget(self.req._(title), self.id)
       
   212 ##         for etype in self.config.etypes(self.req.user, 'read'):
       
   213 ##             view = self.vreg.select_view('list', self.req, self.etype_rset(etype))
       
   214 ##             box.append(self.mk_action(display_name(self.req, etype, 'plural'),
       
   215 ##                                       view.url(), etype=etype))
       
   216 ##         if not box.is_empty():
       
   217 ##             box.render(self.w)
       
   218 
       
   219 
       
   220 class StartupViewsBox(BoxTemplate):
       
   221     """display a box containing links to all startup views"""
       
   222     id = 'startup_views_box'
       
   223     visible = False # disabled by default
       
   224     title = _('startup views')
       
   225     order = 70
       
   226 
       
   227     def call(self, **kwargs):
       
   228         box = BoxWidget(self.req._(self.title), self.id)
       
   229         for view in self.vreg.possible_views(self.req, None):
       
   230             if view.category == 'startupview':
       
   231                 box.append(self.box_action(view))
       
   232         
       
   233         if not box.is_empty():
       
   234             box.render(self.w)
       
   235