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 |