web/views/boxes.py
changeset 6141 b8287e54b528
parent 6140 65a619eb31c4
child 6409 d75535983224
equal deleted inserted replaced
6140:65a619eb31c4 6141:b8287e54b528
    23 Additional boxes (disabled by default):
    23 Additional boxes (disabled by default):
    24 * schema box
    24 * schema box
    25 * possible views box
    25 * possible views box
    26 * startup views box
    26 * startup views box
    27 """
    27 """
       
    28 from __future__ import with_statement
    28 
    29 
    29 __docformat__ = "restructuredtext en"
    30 __docformat__ = "restructuredtext en"
    30 _ = unicode
    31 _ = unicode
    31 
    32 
    32 from warnings import warn
    33 from warnings import warn
    33 
    34 
    34 from logilab.mtconverter import xml_escape
    35 from logilab.mtconverter import xml_escape
    35 from logilab.common.deprecation import class_deprecated
    36 from logilab.common.deprecation import class_deprecated
    36 
    37 
    37 from cubicweb import Unauthorized
    38 from cubicweb import Unauthorized
    38 from cubicweb.selectors import (match_user_groups, match_context, match_kwargs,
    39 from cubicweb.selectors import (match_user_groups, match_kwargs,
    39                                 non_final_entity, nonempty_rset)
    40                                 non_final_entity, nonempty_rset,
       
    41                                 match_context, contextual)
       
    42 from cubicweb.utils import wrap_on_write
    40 from cubicweb.view import EntityView
    43 from cubicweb.view import EntityView
    41 from cubicweb.schema import display_name
    44 from cubicweb.schema import display_name
    42 from cubicweb.web import box, htmlwidgets
    45 from cubicweb.web import component, box, htmlwidgets
    43 
    46 
    44 # XXX bw compat, some cubes import this class from here
    47 # XXX bw compat, some cubes import this class from here
    45 BoxTemplate = box.BoxTemplate
    48 BoxTemplate = box.BoxTemplate
    46 BoxHtml = htmlwidgets.BoxHtml
    49 BoxHtml = htmlwidgets.BoxHtml
    47 
    50 
    48 class EditBox(box.Box): # XXX rename to ActionsBox
    51 class EditBox(component.CtxComponent): # XXX rename to ActionsBox
    49     """
    52     """
    50     box with all actions impacting the entity displayed: edit, copy, delete
    53     box with all actions impacting the entity displayed: edit, copy, delete
    51     change state, add related entities
    54     change state, add related entities
    52     """
    55     """
    53     __regid__ = 'edit_box'
    56     __regid__ = 'edit_box'
    54     __select__ = box.Box.__select__ & non_final_entity()
    57     __select__ = component.CtxComponent.__select__ & non_final_entity()
    55 
    58 
    56     title = _('actions')
    59     title = _('actions')
    57     order = 2
    60     order = 2
    58     contextual = True
    61     contextual = True
    59 
    62 
    87             self._menus_in_order.remove(other_menu)
    90             self._menus_in_order.remove(other_menu)
    88             self._menus_in_order.append(other_menu)
    91             self._menus_in_order.append(other_menu)
    89             for submenu in self._menus_in_order:
    92             for submenu in self._menus_in_order:
    90                 self.add_submenu(self, submenu)
    93                 self.add_submenu(self, submenu)
    91         if not self.items:
    94         if not self.items:
    92             raise box.EmptyComponent()
    95             raise component.EmptyComponent()
    93 
    96 
    94     def render_title(self, w):
    97     def render_title(self, w):
    95         title = self._cw._(self.title)
    98         title = self._cw._(self.title)
    96         if self.cw_rset:
    99         if self.cw_rset:
    97             etypes = self.cw_rset.column_types(0)
   100             etypes = self.cw_rset.column_types(0)
   130             box.append(submenu)
   133             box.append(submenu)
   131         elif appendanyway:
   134         elif appendanyway:
   132             box.append(xml_escape(submenu.label))
   135             box.append(xml_escape(submenu.label))
   133 
   136 
   134 
   137 
   135 class SearchBox(box.Box):
   138 class SearchBox(component.CtxComponent):
   136     """display a box with a simple search form"""
   139     """display a box with a simple search form"""
   137     __regid__ = 'search_box'
   140     __regid__ = 'search_box'
   138 
   141 
   139     title = _('search')
   142     title = _('search')
   140     order = 0
   143     order = 0
   161                           xml_escape(rql), self._cw.next_tabindex()))
   164                           xml_escape(rql), self._cw.next_tabindex()))
   162 
   165 
   163 
   166 
   164 # boxes disabled by default ###################################################
   167 # boxes disabled by default ###################################################
   165 
   168 
   166 class PossibleViewsBox(box.Box):
   169 class PossibleViewsBox(component.CtxComponent):
   167     """display a box containing links to all possible views"""
   170     """display a box containing links to all possible views"""
   168     __regid__ = 'possible_views_box'
   171     __regid__ = 'possible_views_box'
   169 
   172 
   170     visible = False
   173     visible = False
   171     title = _('possible views')
   174     title = _('possible views')
   174     def init_rendering(self):
   177     def init_rendering(self):
   175         self.views = [v for v in self._cw.vreg['views'].possible_views(self._cw,
   178         self.views = [v for v in self._cw.vreg['views'].possible_views(self._cw,
   176                                                                        rset=self.cw_rset)
   179                                                                        rset=self.cw_rset)
   177                       if v.category != 'startupview']
   180                       if v.category != 'startupview']
   178         if not self.views:
   181         if not self.views:
   179             raise box.EmptyComponent()
   182             raise component.EmptyComponent()
   180         self.items = []
   183         self.items = []
   181 
   184 
   182     def render_body(self, w):
   185     def render_body(self, w):
   183         for category, views in box.sort_by_category(self.views):
   186         for category, views in box.sort_by_category(self.views):
   184             menu = htmlwidgets.BoxMenu(category)
   187             menu = htmlwidgets.BoxMenu(category)
   198 
   201 
   199     def init_rendering(self):
   202     def init_rendering(self):
   200         self.views = [v for v in self._cw.vreg['views'].possible_views(self._cw)
   203         self.views = [v for v in self._cw.vreg['views'].possible_views(self._cw)
   201                       if v.category == 'startupview']
   204                       if v.category == 'startupview']
   202         if not self.views:
   205         if not self.views:
   203             raise box.EmptyComponent()
   206             raise component.EmptyComponent()
   204         self.items = []
   207         self.items = []
   205 
   208 
   206 
   209 
   207 class RsetBox(box.Box):
   210 class RsetBox(component.CtxComponent):
   208     """helper view class to display an rset in a sidebox"""
   211     """helper view class to display an rset in a sidebox"""
   209     __select__ = nonempty_rset() & match_kwargs('title', 'vid')
   212     __select__ = nonempty_rset() & match_kwargs('title', 'vid')
   210     __regid__ = 'rsetbox'
   213     __regid__ = 'rsetbox'
   211     cw_property_defs = {}
   214     cw_property_defs = {}
   212     context = 'incontext'
   215     context = 'incontext'
   213 
   216 
   214     @property
   217     @property
   215     def domid(self):
   218     def domid(self):
   216         return super(RsetBox, self).domid + unicode(abs(id(self)))
   219         return super(RsetBox, self).domid + unicode(abs(id(self)))
       
   220 
   217     def render_title(self, w):
   221     def render_title(self, w):
   218         w(self.cw_extra_kwargs['title'])
   222         w(self.cw_extra_kwargs['title'])
   219 
   223 
   220     def render_body(self, w):
   224     def render_body(self, w):
   221         self._cw.view(self.cw_extra_kwargs['vid'], self.cw_rset, w=w)
   225         self._cw.view(self.cw_extra_kwargs['vid'], self.cw_rset, w=w)
   223  # helper classes ##############################################################
   227  # helper classes ##############################################################
   224 
   228 
   225 class SideBoxView(EntityView):
   229 class SideBoxView(EntityView):
   226     """helper view class to display some entities in a sidebox"""
   230     """helper view class to display some entities in a sidebox"""
   227     __metaclass__ = class_deprecated
   231     __metaclass__ = class_deprecated
   228     __deprecation_warning__ = 'SideBoxView is deprecated, use RsetBox instead'
   232     __deprecation_warning__ = '[3.10] SideBoxView is deprecated, use RsetBox instead (%(cls)s)'
   229 
   233 
   230     __regid__ = 'sidebox'
   234     __regid__ = 'sidebox'
   231 
   235 
   232     def call(self, **kwargs):
   236     def call(self, **kwargs):
   233         """display a list of entities by calling their <item_vid> view"""
   237         """display a list of entities by calling their <item_vid> view"""
   234         box = self._cw.vreg['boxes'].select('rsetbox', self._cw, rset=self.cw_rset,
   238         box = self._cw.vreg['ctxcomponents'].select(
   235                                             vid='autolimited', title=title,
   239             'rsetbox', self._cw, rset=self.cw_rset, vid='autolimited',
   236                                             **self.cw_extra_kwargs)
   240             title=title, **self.cw_extra_kwargs)
   237         box.render(self.w)
   241         box.render(self.w)
   238 
   242 
   239 
   243 
   240 class ContextualBoxLayout(box.Layout):
   244 class ContextualBoxLayout(component.Layout):
   241     __select__ = match_context('incontext', 'left', 'right') & box.contextual()
   245     __select__ = match_context('incontext', 'left', 'right') & contextual()
   242     # predefined class in cubicweb.css: contextualBox | contextFreeBox
   246     # predefined class in cubicweb.css: contextualBox | contextFreeBox
   243     # XXX: navigationBox | actionBox
   247     # XXX: navigationBox | actionBox
   244     cssclass = 'contextualBox'
   248     cssclass = 'contextualBox'
   245 
   249 
   246     def render(self, w):
   250     def render(self, w):
   247         view = self.cw_extra_kwargs['view']
   251         if self.init_rendering():
   248         try:
   252             view = self.cw_extra_kwargs['view']
   249             view.init_rendering()
   253             w(u'<div class="%s %s" id="%s">' % (self.cssclass, view.cssclass,
   250         except Unauthorized, ex:
   254                                                 view.domid))
   251             self.warning("can't render %s: %s", view, ex)
   255             with wrap_on_write(w, u'<div class="boxTitle"><span>',
   252             return
   256                                u'</span></div>') as wow:
   253         except box.EmptyComponent:
   257                 view.render_title(wow)
   254             return
   258             w(u'<div class="boxBody">')
   255         w(u'<div class="%s %s" id="%s">' % (self.cssclass, view.cssclass,
   259             view.render_body(w)
   256                                             view.domid))
   260             # boxFooter div is a CSS place holder (for shadow for example)
   257         w(u'<div class="boxTitle"><span>')
   261             w(u'</div><div class="boxFooter"></div></div>\n')
   258         view.render_title(w)
       
   259         w(u'</span></div>\n<div class="boxBody">')
       
   260         view.render_body(w)
       
   261         # boxFooter div is a CSS place holder (for shadow for example)
       
   262         w(u'</div><div class="boxFooter"></div></div>\n')
       
   263 
   262 
   264 
   263 
   265 class ContextFreeBoxLayout(ContextualBoxLayout):
   264 class ContextFreeBoxLayout(ContextualBoxLayout):
   266     __select__ = match_context('incontext', 'left', 'right') & ~box.contextual()
   265     __select__ = match_context('incontext', 'left', 'right') & ~contextual()
   267     cssclass = 'contextFreeBox'
   266     cssclass = 'contextFreeBox'