182 @property |
186 @property |
183 def related_limit(self): |
187 def related_limit(self): |
184 if self.force_display: |
188 if self.force_display: |
185 return None |
189 return None |
186 return self.maxrelitems + 1 |
190 return self.maxrelitems + 1 |
187 |
|
188 def relations_by_category(self, categories=None, permission=None): |
|
189 """return a list of (relation schema, target schemas, role) matching |
|
190 given category(ies) and permission |
|
191 """ |
|
192 return self.erelations_by_category(self.edited_entity, categories, |
|
193 permission) |
|
194 |
|
195 def inlined_relations(self): |
|
196 """return a list of (relation schema, target schemas, role) matching |
|
197 given category(ies) and permission |
|
198 """ |
|
199 # we'll need an initialized varmaker if there are some inlined relation |
|
200 self.initialize_varmaker() |
|
201 return self.erelations_by_category(self.edited_entity, True, 'add', |
|
202 self.rinlined) |
|
203 |
|
204 def srelations_by_category(self, categories=None, permission=None, |
|
205 strict=False): |
|
206 """filter out result of relations_by_category(categories, permission) by |
|
207 removing final relations |
|
208 |
|
209 return a sorted list of (relation's label, relation'schema, role) |
|
210 """ |
|
211 return self.esrelations_by_category(self.edited_entity, categories, |
|
212 permission, strict=strict) |
|
213 |
|
214 def action(self): |
|
215 """return the form's action attribute. Default to validateform if not |
|
216 explicitly overriden. |
|
217 """ |
|
218 try: |
|
219 return self._action |
|
220 except AttributeError: |
|
221 return self.build_url('validateform') |
|
222 |
|
223 def set_action(self, value): |
|
224 """override default action""" |
|
225 self._action = value |
|
226 |
|
227 action = property(action, set_action) |
|
228 |
|
229 def editable_attributes(self): |
|
230 """return a list of (relation schema, role) to edit for the entity""" |
|
231 return [(rschema, role) for rschema, _, role in self.relations_by_category( |
|
232 self.attrcategories, 'add') if rschema != 'eid'] |
|
233 |
|
234 def relations_table(self): |
|
235 """yiels 3-tuples (rtype, target, related_list) |
|
236 where <related_list> itself a list of : |
|
237 - node_id (will be the entity element's DOM id) |
|
238 - appropriate javascript's togglePendingDelete() function call |
|
239 - status 'pendingdelete' or '' |
|
240 - oneline view of related entity |
|
241 """ |
|
242 entity = self.edited_entity |
|
243 pending_deletes = self.req.get_pending_deletes(entity.eid) |
|
244 for label, rschema, role in self.srelations_by_category('generic', 'add', |
|
245 strict=True): |
|
246 relatedrset = entity.related(rschema, role, limit=self.related_limit) |
|
247 if rschema.has_perm(self.req, 'delete'): |
|
248 toggleable_rel_link_func = editforms.toggleable_relation_link |
|
249 else: |
|
250 toggleable_rel_link_func = lambda x, y, z: u'' |
|
251 related = [] |
|
252 for row in xrange(relatedrset.rowcount): |
|
253 nodeid = editforms.relation_id(entity.eid, rschema, role, |
|
254 relatedrset[row][0]) |
|
255 if nodeid in pending_deletes: |
|
256 status = u'pendingDelete' |
|
257 label = '+' |
|
258 else: |
|
259 status = u'' |
|
260 label = 'x' |
|
261 dellink = toggleable_rel_link_func(entity.eid, nodeid, label) |
|
262 eview = self.view('oneline', relatedrset, row=row) |
|
263 related.append((nodeid, dellink, status, eview)) |
|
264 yield (rschema, role, related) |
|
265 |
|
266 def restore_pending_inserts(self, cell=False): |
|
267 """used to restore edition page as it was before clicking on |
|
268 'search for <some entity type>' |
|
269 """ |
|
270 eid = self.edited_entity.eid |
|
271 cell = cell and "div_insert_" or "tr" |
|
272 pending_inserts = set(self.req.get_pending_inserts(eid)) |
|
273 for pendingid in pending_inserts: |
|
274 eidfrom, rtype, eidto = pendingid.split(':') |
|
275 if typed_eid(eidfrom) == eid: # subject |
|
276 label = display_name(self.req, rtype, 'subject') |
|
277 reid = eidto |
|
278 else: |
|
279 label = display_name(self.req, rtype, 'object') |
|
280 reid = eidfrom |
|
281 jscall = "javascript: cancelPendingInsert('%s', '%s', null, %s);" \ |
|
282 % (pendingid, cell, eid) |
|
283 rset = self.req.eid_rset(reid) |
|
284 eview = self.view('text', rset, row=0) |
|
285 # XXX find a clean way to handle baskets |
|
286 if rset.description[0][0] == 'Basket': |
|
287 eview = '%s (%s)' % (eview, display_name(self.req, 'Basket')) |
|
288 yield rtype, pendingid, jscall, label, reid, eview |
|
289 |
|
290 # should_* method extracted to allow overriding |
|
291 |
|
292 def should_inline_relation_form(self, rschema, targettype, role): |
|
293 """return true if the given relation with entity has role and a |
|
294 targettype target should be inlined |
|
295 """ |
|
296 return self.rinlined.etype_get(self.edited_entity.id, rschema, role, |
|
297 targettype) |
|
298 |
|
299 def display_inline_edition_form(self, w, rschema, targettype, role, |
|
300 i18nctx): |
|
301 """display inline forms for already related entities. |
|
302 |
|
303 Return True if some inlined form are actually displayed |
|
304 """ |
|
305 existant = False |
|
306 entity = self.edited_entity |
|
307 related = entity.has_eid() and entity.related(rschema, role) |
|
308 if related: |
|
309 # display inline-edition view for all existing related entities |
|
310 for i, relentity in enumerate(related.entities()): |
|
311 if relentity.has_perm('update'): |
|
312 w(self.view('inline-edition', related, row=i, col=0, |
|
313 rtype=rschema, role=role, ptype=entity.e_schema, |
|
314 peid=entity.eid, i18nctx=i18nctx)) |
|
315 existant = True |
|
316 return existant |
|
317 |
|
318 def should_display_inline_creation_form(self, rschema, existant, card): |
|
319 """return true if a creation form should be inlined |
|
320 |
|
321 by default true if there is no related entity and we need at least one |
|
322 """ |
|
323 return not existant and card in '1+' or self.req.form.has_key('force_%s_display' % rschema) |
|
324 |
|
325 def display_inline_creation_form(self, w, rschema, targettype, role, |
|
326 i18nctx): |
|
327 entity = self.edited_entity |
|
328 w(self.view('inline-creation', None, etype=targettype, |
|
329 peid=entity.eid, ptype=entity.e_schema, |
|
330 rtype=rschema, role=role, i18nctx=i18nctx)) |
|
331 |
|
332 def should_display_add_new_relation_link(self, rschema, existant, card): |
|
333 """return true if we should add a link to add a new creation form |
|
334 (through ajax call) |
|
335 |
|
336 by default true if there is no related entity or if the relation has |
|
337 multiple cardinality |
|
338 """ |
|
339 return not existant or card in '+*' |
|
340 |
|
341 def should_hide_add_new_relation_link(self, rschema, card): |
|
342 """return true if once an inlined creation form is added, the 'add new' |
|
343 link should be hidden |
|
344 |
|
345 by default true if the relation has single cardinality |
|
346 """ |
|
347 return card in '1?' |
|
348 |
191 |
349 @property |
192 @property |
350 def form_needs_multipart(self): |
193 def form_needs_multipart(self): |
351 """true if the form needs enctype=multipart/form-data""" |
194 """true if the form needs enctype=multipart/form-data""" |
352 if super(AutomaticEntityForm, self).form_needs_multipart: |
195 if super(AutomaticEntityForm, self).form_needs_multipart: |
371 entity = self.vreg['etypes'].etype_class(targettype)(self.req) |
214 entity = self.vreg['etypes'].etype_class(targettype)(self.req) |
372 subform = self.vreg['forms'].select('edition', self.req, entity=entity) |
215 subform = self.vreg['forms'].select('edition', self.req, entity=entity) |
373 if subform.form_needs_multipart: |
216 if subform.form_needs_multipart: |
374 return True |
217 return True |
375 return False |
218 return False |
|
219 |
|
220 def action(self): |
|
221 """return the form's action attribute. Default to validateform if not |
|
222 explicitly overriden. |
|
223 """ |
|
224 try: |
|
225 return self._action |
|
226 except AttributeError: |
|
227 return self.build_url('validateform') |
|
228 |
|
229 def set_action(self, value): |
|
230 """override default action""" |
|
231 self._action = value |
|
232 |
|
233 action = property(action, set_action) |
|
234 |
|
235 # methods mapping edited entity relations to fields in the form ############ |
|
236 |
|
237 def relations_by_category(self, categories=None, permission=None): |
|
238 """return a list of (relation schema, target schemas, role) matching |
|
239 given category(ies) and permission |
|
240 """ |
|
241 return self.erelations_by_category(self.edited_entity, categories, |
|
242 permission) |
|
243 |
|
244 def inlined_relations(self): |
|
245 """return a list of (relation schema, target schemas, role) matching |
|
246 given category(ies) and permission |
|
247 """ |
|
248 # we'll need an initialized varmaker if there are some inlined relation |
|
249 self.initialize_varmaker() |
|
250 return self.erelations_by_category(self.edited_entity, True, 'add', |
|
251 self.rinlined) |
|
252 |
|
253 def srelations_by_category(self, categories=None, permission=None, |
|
254 strict=False): |
|
255 """filter out result of relations_by_category(categories, permission) by |
|
256 removing final relations |
|
257 |
|
258 return a sorted list of (relation's label, relation'schema, role) |
|
259 """ |
|
260 return self.esrelations_by_category(self.edited_entity, categories, |
|
261 permission, strict=strict) |
|
262 |
|
263 def editable_attributes(self): |
|
264 """return a list of (relation schema, role) to edit for the entity""" |
|
265 return [(rschema, role) for rschema, _, role in self.relations_by_category( |
|
266 self.attrcategories, 'add') if rschema != 'eid'] |
|
267 |
|
268 # generic relations modifier ############################################### |
|
269 |
|
270 def relations_table(self): |
|
271 """yiels 3-tuples (rtype, target, related_list) |
|
272 where <related_list> itself a list of : |
|
273 - node_id (will be the entity element's DOM id) |
|
274 - appropriate javascript's togglePendingDelete() function call |
|
275 - status 'pendingdelete' or '' |
|
276 - oneline view of related entity |
|
277 """ |
|
278 entity = self.edited_entity |
|
279 pending_deletes = self.req.get_pending_deletes(entity.eid) |
|
280 for label, rschema, role in self.srelations_by_category('generic', 'add', |
|
281 strict=True): |
|
282 relatedrset = entity.related(rschema, role, limit=self.related_limit) |
|
283 if rschema.has_perm(self.req, 'delete'): |
|
284 toggleable_rel_link_func = editforms.toggleable_relation_link |
|
285 else: |
|
286 toggleable_rel_link_func = lambda x, y, z: u'' |
|
287 related = [] |
|
288 for row in xrange(relatedrset.rowcount): |
|
289 nodeid = editforms.relation_id(entity.eid, rschema, role, |
|
290 relatedrset[row][0]) |
|
291 if nodeid in pending_deletes: |
|
292 status = u'pendingDelete' |
|
293 label = '+' |
|
294 else: |
|
295 status = u'' |
|
296 label = 'x' |
|
297 dellink = toggleable_rel_link_func(entity.eid, nodeid, label) |
|
298 eview = self.view('oneline', relatedrset, row=row) |
|
299 related.append((nodeid, dellink, status, eview)) |
|
300 yield (rschema, role, related) |
|
301 |
|
302 def restore_pending_inserts(self, cell=False): |
|
303 """used to restore edition page as it was before clicking on |
|
304 'search for <some entity type>' |
|
305 """ |
|
306 eid = self.edited_entity.eid |
|
307 cell = cell and "div_insert_" or "tr" |
|
308 pending_inserts = set(self.req.get_pending_inserts(eid)) |
|
309 for pendingid in pending_inserts: |
|
310 eidfrom, rtype, eidto = pendingid.split(':') |
|
311 if typed_eid(eidfrom) == eid: # subject |
|
312 label = display_name(self.req, rtype, 'subject') |
|
313 reid = eidto |
|
314 else: |
|
315 label = display_name(self.req, rtype, 'object') |
|
316 reid = eidfrom |
|
317 jscall = "javascript: cancelPendingInsert('%s', '%s', null, %s);" \ |
|
318 % (pendingid, cell, eid) |
|
319 rset = self.req.eid_rset(reid) |
|
320 eview = self.view('text', rset, row=0) |
|
321 # XXX find a clean way to handle baskets |
|
322 if rset.description[0][0] == 'Basket': |
|
323 eview = '%s (%s)' % (eview, display_name(self.req, 'Basket')) |
|
324 yield rtype, pendingid, jscall, label, reid, eview |
|
325 |
|
326 # inlined forms support #################################################### |
|
327 |
|
328 def should_inline_relation_form(self, rschema, targettype, role): |
|
329 """return true if the given relation with entity has role and a |
|
330 targettype target should be inlined |
|
331 """ |
|
332 return self.rinlined.etype_get(self.edited_entity.id, rschema, role, |
|
333 targettype) |
|
334 |
|
335 def display_inline_edition_form(self, w, rschema, targettype, role, |
|
336 i18nctx): |
|
337 """display inline forms for already related entities. |
|
338 |
|
339 Return True if some inlined form are actually displayed |
|
340 """ |
|
341 existant = False |
|
342 entity = self.edited_entity |
|
343 related = entity.has_eid() and entity.related(rschema, role) |
|
344 if related: |
|
345 # display inline-edition view for all existing related entities |
|
346 for i, relentity in enumerate(related.entities()): |
|
347 if relentity.has_perm('update'): |
|
348 w(self.view('inline-edition', related, row=i, col=0, |
|
349 rtype=rschema, role=role, ptype=entity.e_schema, |
|
350 peid=entity.eid, i18nctx=i18nctx)) |
|
351 existant = True |
|
352 return existant |
|
353 |
|
354 def should_display_inline_creation_form(self, rschema, existant, card): |
|
355 """return true if a creation form should be inlined |
|
356 |
|
357 by default true if there is no related entity and we need at least one |
|
358 """ |
|
359 return not existant and card in '1+' or self.req.form.has_key('force_%s_display' % rschema) |
|
360 |
|
361 def display_inline_creation_form(self, w, rschema, targettype, role, |
|
362 i18nctx): |
|
363 """display inline forms to a newly related (hence created) entity. |
|
364 |
|
365 Return True if some inlined form are actually displayed |
|
366 """ |
|
367 entity = self.edited_entity |
|
368 w(self.view('inline-creation', None, etype=targettype, |
|
369 peid=entity.eid, ptype=entity.e_schema, |
|
370 rtype=rschema, role=role, i18nctx=i18nctx)) |
|
371 |
|
372 def should_display_add_new_relation_link(self, rschema, existant, card): |
|
373 """return true if we should add a link to add a new creation form |
|
374 (through ajax call) |
|
375 |
|
376 by default true if there is no related entity or if the relation has |
|
377 multiple cardinality |
|
378 """ |
|
379 return not existant or card in '+*' |
|
380 |
|
381 def should_hide_add_new_relation_link(self, rschema, card): |
|
382 """return true if once an inlined creation form is added, the 'add new' |
|
383 link should be hidden |
|
384 |
|
385 by default true if the relation has single cardinality |
|
386 """ |
|
387 return card in '1?' |
|
388 |
376 |
389 |
377 def etype_relation_field(etype, rtype, role='subject'): |
390 def etype_relation_field(etype, rtype, role='subject'): |
378 eschema = AutomaticEntityForm.schema.eschema(etype) |
391 eschema = AutomaticEntityForm.schema.eschema(etype) |
379 return AutomaticEntityForm.field_by_name(rtype, role, eschema) |
392 return AutomaticEntityForm.field_by_name(rtype, role, eschema) |
380 |
393 |