web/box.py
branchtls-sprint
changeset 1493 8270580b65a0
parent 1422 51b3fd55784e
child 1624 baf484a182cd
equal deleted inserted replaced
1492:53d28ac02868 1493:8270580b65a0
    22 _ = unicode
    22 _ = unicode
    23 
    23 
    24 
    24 
    25 class BoxTemplate(View):
    25 class BoxTemplate(View):
    26     """base template for boxes, usually a (contextual) list of possible
    26     """base template for boxes, usually a (contextual) list of possible
    27     
    27 
    28     actions. Various classes attributes may be used to control the box
    28     actions. Various classes attributes may be used to control the box
    29     rendering.
    29     rendering.
    30     
    30 
    31     You may override on of the formatting callbacks is this is not necessary
    31     You may override on of the formatting callbacks is this is not necessary
    32     for your custom box.
    32     for your custom box.
    33     
    33 
    34     Classes inheriting from this class usually only have to override call
    34     Classes inheriting from this class usually only have to override call
    35     to fetch desired actions, and then to do something like  ::
    35     to fetch desired actions, and then to do something like  ::
    36 
    36 
    37         box.render(self.w)
    37         box.render(self.w)
    38     """
    38     """
    39     __registry__ = 'boxes'
    39     __registry__ = 'boxes'
    40     __select__ = match_context_prop()
    40     __select__ = match_context_prop()
    41     registered = classmethod(require_group_compat(View.registered))
    41     registered = classmethod(require_group_compat(View.registered))
    42     
    42 
    43     categories_in_order = ()
    43     categories_in_order = ()
    44     property_defs = {
    44     property_defs = {
    45         _('visible'): dict(type='Boolean', default=True,
    45         _('visible'): dict(type='Boolean', default=True,
    46                            help=_('display the box or not')),
    46                            help=_('display the box or not')),
    47         _('order'):   dict(type='Int', default=99,
    47         _('order'):   dict(type='Int', default=99,
    74         .format_actions method
    74         .format_actions method
    75         """
    75         """
    76         if escape:
    76         if escape:
    77             title = html_escape(title)
    77             title = html_escape(title)
    78         return self.box_action(self._action(title, path, **kwargs))
    78         return self.box_action(self._action(title, path, **kwargs))
    79     
    79 
    80     def _action(self, title, path, **kwargs):
    80     def _action(self, title, path, **kwargs):
    81         return UnregisteredAction(self.req, self.rset, title, path, **kwargs)        
    81         return UnregisteredAction(self.req, self.rset, title, path, **kwargs)
    82 
    82 
    83     # formating callbacks
    83     # formating callbacks
    84 
    84 
    85     def boxitem_link_tooltip(self, action):
    85     def boxitem_link_tooltip(self, action):
    86         if action.id:
    86         if action.id:
    89 
    89 
    90     def box_action(self, action):
    90     def box_action(self, action):
    91         cls = getattr(action, 'html_class', lambda: None)() or self.htmlitemclass
    91         cls = getattr(action, 'html_class', lambda: None)() or self.htmlitemclass
    92         return BoxLink(action.url(), self.req._(action.title),
    92         return BoxLink(action.url(), self.req._(action.title),
    93                        cls, self.boxitem_link_tooltip(action))
    93                        cls, self.boxitem_link_tooltip(action))
    94         
    94 
    95 
    95 
    96 class RQLBoxTemplate(BoxTemplate):
    96 class RQLBoxTemplate(BoxTemplate):
    97     """abstract box for boxes displaying the content of a rql query not
    97     """abstract box for boxes displaying the content of a rql query not
    98     related to the current result set.
    98     related to the current result set.
    99     
    99 
   100     It rely on etype, rtype (both optional, usable to control registration
   100     It rely on etype, rtype (both optional, usable to control registration
   101     according to application schema and display according to connected
   101     according to application schema and display according to connected
   102     user's rights) and rql attributes
   102     user's rights) and rql attributes
   103     """
   103     """
   104 #XXX    __selectors__ = BoxTemplate.__selectors__ + (etype_rtype_selector,)
   104 #XXX    __selectors__ = BoxTemplate.__selectors__ + (etype_rtype_selector,)
   105 
   105 
   106     rql  = None
   106     rql  = None
   107     
   107 
   108     def to_display_rql(self):
   108     def to_display_rql(self):
   109         assert self.rql is not None, self.id
   109         assert self.rql is not None, self.id
   110         return (self.rql,)
   110         return (self.rql,)
   111     
   111 
   112     def call(self, **kwargs):
   112     def call(self, **kwargs):
   113         try:
   113         try:
   114             rset = self.req.execute(*self.to_display_rql())
   114             rset = self.req.execute(*self.to_display_rql())
   115         except Unauthorized:
   115         except Unauthorized:
   116             # can't access to something in the query, forget this box
   116             # can't access to something in the query, forget this box
   121         for i, (teid, tname) in enumerate(rset):
   121         for i, (teid, tname) in enumerate(rset):
   122             entity = rset.get_entity(i, 0)
   122             entity = rset.get_entity(i, 0)
   123             box.append(self.mk_action(tname, entity.absolute_url()))
   123             box.append(self.mk_action(tname, entity.absolute_url()))
   124         box.render(w=self.w)
   124         box.render(w=self.w)
   125 
   125 
   126         
   126 
   127 class UserRQLBoxTemplate(RQLBoxTemplate):
   127 class UserRQLBoxTemplate(RQLBoxTemplate):
   128     """same as rql box template but the rql is build using the eid of the
   128     """same as rql box template but the rql is build using the eid of the
   129     request's user
   129     request's user
   130     """
   130     """
   131 
   131 
   132     def to_display_rql(self):
   132     def to_display_rql(self):
   133         assert self.rql is not None, self.id
   133         assert self.rql is not None, self.id
   134         return (self.rql, {'x': self.req.user.eid}, 'x')
   134         return (self.rql, {'x': self.req.user.eid}, 'x')
   135     
   135 
   136 
   136 
   137 class EntityBoxTemplate(BoxTemplate):
   137 class EntityBoxTemplate(BoxTemplate):
   138     """base class for boxes related to a single entity"""
   138     """base class for boxes related to a single entity"""
   139     __select__ = BoxTemplate.__select__ & one_line_rset() & primary_view()
   139     __select__ = BoxTemplate.__select__ & one_line_rset() & primary_view()
   140     registered = accepts_compat(has_relation_compat(condition_compat(BoxTemplate.registered)))
   140     registered = accepts_compat(has_relation_compat(condition_compat(BoxTemplate.registered)))
   141     context = 'incontext'
   141     context = 'incontext'
   142     
   142 
   143     def call(self, row=0, col=0, **kwargs):
   143     def call(self, row=0, col=0, **kwargs):
   144         """classes inheriting from EntityBoxTemplate should define cell_call"""
   144         """classes inheriting from EntityBoxTemplate should define cell_call"""
   145         self.cell_call(row, col, **kwargs)
   145         self.cell_call(row, col, **kwargs)
   146 
   146 
   147 
   147 
   163     by a given relation
   163     by a given relation
   164 
   164 
   165     subclasses should define at least id, rtype and target
   165     subclasses should define at least id, rtype and target
   166     class attributes.
   166     class attributes.
   167     """
   167     """
   168     
   168 
   169     def cell_call(self, row, col, view=None):
   169     def cell_call(self, row, col, view=None):
   170         self.req.add_js('cubicweb.ajax.js')
   170         self.req.add_js('cubicweb.ajax.js')
   171         entity = self.entity(row, col)
   171         entity = self.entity(row, col)
   172         box = SideBoxWidget(display_name(self.req, self.rtype), self.id)
   172         box = SideBoxWidget(display_name(self.req, self.rtype), self.id)
   173         count = self.w_related(box, entity)
   173         count = self.w_related(box, entity)
   176         self.w_unrelated(box, entity)
   176         self.w_unrelated(box, entity)
   177         box.render(self.w)
   177         box.render(self.w)
   178 
   178 
   179     def div_id(self):
   179     def div_id(self):
   180         return self.id
   180         return self.id
   181         
   181 
   182     def box_item(self, entity, etarget, rql, label):
   182     def box_item(self, entity, etarget, rql, label):
   183         """builds HTML link to edit relation between `entity` and `etarget`
   183         """builds HTML link to edit relation between `entity` and `etarget`
   184         """
   184         """
   185         role, target = get_role(self), get_target(self)
   185         role, target = get_role(self), get_target(self)
   186         args = {role[0] : entity.eid, target[0] : etarget.eid}
   186         args = {role[0] : entity.eid, target[0] : etarget.eid}
   187         url = self.user_rql_callback((rql, args))
   187         url = self.user_rql_callback((rql, args))
   188         # for each target, provide a link to edit the relation
   188         # for each target, provide a link to edit the relation
   189         label = u'[<a href="%s">%s</a>] %s' % (url, label,
   189         label = u'[<a href="%s">%s</a>] %s' % (url, label,
   190                                                etarget.view('incontext'))
   190                                                etarget.view('incontext'))
   191         return RawBoxItem(label, liclass=u'invisible')
   191         return RawBoxItem(label, liclass=u'invisible')
   192     
   192 
   193     def w_related(self, box, entity):
   193     def w_related(self, box, entity):
   194         """appends existing relations to the `box`"""
   194         """appends existing relations to the `box`"""
   195         rql = 'DELETE S %s O WHERE S eid %%(s)s, O eid %%(o)s' % self.rtype
   195         rql = 'DELETE S %s O WHERE S eid %%(s)s, O eid %%(o)s' % self.rtype
   196         related = self.related_entities(entity)
   196         related = self.related_entities(entity)
   197         for etarget in related:
   197         for etarget in related:
   198             box.append(self.box_item(entity, etarget, rql, u'-'))
   198             box.append(self.box_item(entity, etarget, rql, u'-'))
   199         return len(related)
   199         return len(related)
   200     
   200 
   201     def w_unrelated(self, box, entity):
   201     def w_unrelated(self, box, entity):
   202         """appends unrelated entities to the `box`"""
   202         """appends unrelated entities to the `box`"""
   203         rql = 'SET S %s O WHERE S eid %%(s)s, O eid %%(o)s' % self.rtype
   203         rql = 'SET S %s O WHERE S eid %%(s)s, O eid %%(o)s' % self.rtype
   204         for etarget in self.unrelated_entities(entity):
   204         for etarget in self.unrelated_entities(entity):
   205             box.append(self.box_item(entity, etarget, rql, u'+'))
   205             box.append(self.box_item(entity, etarget, rql, u'+'))
   218         for _, eid in entity.vocabulary(self.rtype, get_role(self)):
   218         for _, eid in entity.vocabulary(self.rtype, get_role(self)):
   219             if eid is not None:
   219             if eid is not None:
   220                 rset = self.req.eid_rset(eid)
   220                 rset = self.req.eid_rset(eid)
   221                 entities.append(rset.get_entity(0, 0))
   221                 entities.append(rset.get_entity(0, 0))
   222         return entities
   222         return entities
   223         
   223 
   224     def related_entities(self, entity):
   224     def related_entities(self, entity):
   225         return entity.related(self.rtype, get_role(self), entities=True)
   225         return entity.related(self.rtype, get_role(self), entities=True)
   226 
   226