def _get_removejs(self): """ Don't display the remove link in edition form if the cardinality is 1. Handled in InlineEntityCreationFormView for creation form. """ entity = self._entity() rdef = entity.e_schema.rdef(self.rtype, neg_role(self.role), self.petype) card = rdef.role_cardinality(self.role) if card == '1': # don't display remove link return None # if cardinality is 1..n (+), dont display link to remove an inlined form for the first form # allowing to edit the relation. To detect so: # # * if parent form (pform) is None, we're generated through an ajax call and so we know this # is not the first form # # * if parent form is not None, look for previous InlinedFormField in the parent's form # fields if card == '+' and self.pform is not None: # retrieve all field'views handling this relation and return None if we're the first of # them first_view = next( iter((f.view for f in self.pform.fields if isinstance(f, InlinedFormField) and f.view.rtype == self.rtype and f.view.role == self.role))) if self == first_view: return None return self.removejs and self.removejs % (self.peid, self.rtype, entity.eid)
def _update_entity_rel_cache_add(cnx, entity, rtype, role, targetentity, otherside=False): rcache = entity.cw_relation_cached(rtype, role) if rcache is None: rset = ResultSet([[targetentity.eid]], 'Any X WHERE X eid %s' % targetentity.eid, description=[[targetentity.cw_etype]]) rset.req = cnx entities = [] else: rset, entities = rcache rset = rset.copy() entities = list(entities) rset.rows.append([targetentity.eid]) if not isinstance(rset.description, list): # else description not set rset.description = list(rset.description) rset.description.append([targetentity.cw_etype]) if targetentity.cw_rset is None: targetentity.cw_rset = rset targetentity.cw_row = rset.rowcount targetentity.cw_col = 0 rset.rowcount += 1 entities.append(targetentity) entity._cw_related_cache['%s_%s' % (rtype, role)] = (rset, tuple(entities)) if not otherside: _update_entity_rel_cache_add(cnx, targetentity, rtype, neg_role(role), entity, True)
def __call__(self): eid = self.entity.eid for rdef, role in self.entity.e_schema.composite_rdef_roles: rtype = rdef.rtype.type target = getattr(rdef, neg_role(role)) expr = ('C %s X' % rtype) if role == 'subject' else ('X %s C' % rtype) self._cw.execute( 'DELETE %s X WHERE C eid %%(c)s, %s' % (target, expr), {'c': eid})
def add_hiddens(self, form, entity): """to ease overriding (see cubes.vcsfile.views.forms for instance)""" iid = 'rel-%s-%s-%s' % (self.peid, self.rtype, entity.eid) # * str(self.rtype) in case it's a schema object # * neged_role() since role is the for parent entity, we want the role # of the inlined entity form.add_hidden(name=str(self.rtype), value=self.peid, role=neg_role(self.role), eidparam=True, id=iid)
def parent(self): if needs_container_parent(self.entity.e_schema): parent = self.entity.container_parent return parent[0] if parent else None try: rdef = first_parent_rdef(self.entity.e_schema) rtype, role = rdef.rtype.type, neg_role(rdef.composite) except StopIteration: self.debug('ContainerProtocol.parent stopped on %s', self.entity) return None parent = self.entity.related(rtype=rtype, role=role, entities=True) if parent: return parent[0]
def iterrdefs(eschema, meta=True, final=True, skiprtypes=(), skipetypes=()): """ yield all the relation definitions of an entity type """ for role in ('subject', 'object'): rschemas = eschema.subjrels if role == 'subject' else eschema.objrels for rschema in rschemas: if not meta and rschema.meta: continue if not final and rschema.final: continue if rschema in skiprtypes: continue for rdef in rschema.rdefs.itervalues(): if getattr(rdef, neg_role(role)) in skipetypes: continue if getattr(rdef, role) == eschema: yield rdef
def removejs(self): entity = self._entity() rdef = entity.e_schema.rdef(self.rtype, neg_role(self.role), self.petype) card = rdef.role_cardinality(self.role) # when one is adding an inline entity for a relation of a single card, # the 'add a new xxx' link disappears. If the user then cancel the addition, # we have to make this link appears back. This is done by giving add new link # id to removeInlineForm. if card == '?': divid = "addNew%s%s%s:%s" % (self.etype, self.rtype, self.role, self.peid) return "removeInlineForm('%%s', '%%s', '%s', '%%s', '%s')" % ( self.role, divid) elif card in '+*': return "removeInlineForm('%%s', '%%s', '%s', '%%s')" % self.role
def _init(self, sschema, rschema, oschema, role): if self.get(sschema, rschema, oschema, role) is None: rdef = rschema.rdef(sschema, oschema) if rschema.final: if rschema.meta or sschema.is_metadata(rschema) \ or oschema.type in ('Password', 'Bytes'): section = 'hidden' else: section = 'attributes' else: if rdef.role_cardinality(role) in '1+': section = 'attributes' elif rdef.composite == neg_role(role): section = 'relations' else: section = 'sideboxes' self.tag_relation((sschema, rschema, oschema, role), section)
def _prepare_form(self, entity, rschema, role, action): assert action in ('edit_rtype', 'edit_related', 'add', 'delete'), action if action == 'edit_rtype': return False, entity label = True if action in ('edit_related', 'delete'): edit_entity = entity.related(rschema, role).get_entity(0, 0) elif action == 'add': add_etype = self._compute_ttypes(rschema, role)[0] _new_entity = self._cw.vreg['etypes'].etype_class(add_etype)( self._cw) _new_entity.eid = next(self._cw.varmaker) edit_entity = _new_entity # XXX see forms.py ~ 276 and entities.linked_to method # is there another way? self._cw.form['__linkto'] = '%s:%s:%s' % (rschema, entity.eid, neg_role(role)) assert edit_entity return label, edit_entity
def linked_to(self): linked_to = {} # case where this is an embeded creation form try: eid = int(self.cw_extra_kwargs['peid']) except (KeyError, ValueError): # When parent is being created, its eid is not numeric (e.g. 'A') # hence ValueError. pass else: ltrtype = self.cw_extra_kwargs['rtype'] ltrole = neg_role(self.cw_extra_kwargs['role']) linked_to[(ltrtype, ltrole)] = [eid] # now consider __linkto if the current form is the main form try: self.field_by_name('__maineid') except form.FieldNotFound: return linked_to for linkto in self._cw.list_form_param('__linkto'): ltrtype, eid, ltrole = linkto.split(':') linked_to.setdefault((ltrtype, ltrole), []).append(int(eid)) return linked_to
def render(self, form, field, renderer): stream = [] w = stream.append req = form._cw _ = req._ __ = _ entity = form.edited_entity eid = entity.eid etype = entity.e_schema relative_url = '%s' % eid w(u'<div class="accordion form-relation" id="accordion_%s">' % eid) w_num_rel = 0 for label, rschema, role in field.relations: # FIXME should be a more optimized way to get the name of # the target entity. targets = rschema.targets(etype, role) #---- # Agregar ciclo sobre targets #---- for target in targets: if req.vreg.schema.eschema(target).has_perm(req, 'read'): if w_num_rel == 0: w(u'<div class="row-fluid">') w_num_rel = w_num_rel + 1 linkto = '%s:%s:%s' % (rschema, eid, neg_role(role)) link_label = u'%s %s' % (req._('add'), req._(target)) lsearch = u'%s %s' % (req._('search'), req._(target)) relate_entity = ( u'<div class="relate-entity">%(relate_entity)s</div>' % { 'relate_entity': entity.view('autocomplete-edition-view', relation=rschema, role=role, etype_search=target, showname="E") }) search_url = ( '<a href="?__mode=%(role)s:%(eid)s:%(rschema)s:' '%(target)s&vid=search-associate"' 'class="btn btn-micro btn-link">' '%(link_label)s' '</a>' % { 'role': role, 'rschema': rschema, 'eid': eid, 'link_label': lsearch, 'target': target }) add_new = ( '<a href="/add/%(target)s?__linkto=%(linkto)s' '&__redirectpath=%(url)s&__redirectvid=edition "' 'class="btn btn-micro btn-link">' '%(link_label)s' '</a>' % { 'linkto': linkto, 'url': relative_url, 'link_label': link_label, 'target': target }) w( u'<div class="span4"><div class="accordion-group">' u'<div class="accordion-heading container-fluid">' u' <div id="RDR_%(relation_name)s_%(role)s_%(target)s">' u' <a class="accordion-toggle" data-toggle="collapse" ' u' data-parent="# accordion_%(eid)s" ' u' href="#collapse_%(relation_name)s">' u' %(label)s' u' </a>' u' </div>' # </div> de id_RDR.. u' <div id="add-relation-combo">%(search)s %(add_new)s</div>' u'</div>' # </div> de accordion-heading % { 'eid': eid, 'relation_name': rschema, 'target': target, 'label': label, 'search': search_url, 'add_new': add_new, 'role': role, 'relate-entity': relate_entity }) w(u'<div id="collapse_%(relation)s" class="accordion-body collapse in">' u' <div class="accordion-inner">' u' <ul class="thumbnails">%(relate-entity)s' % { 'relation': rschema, 'relate-entity': relate_entity }) #---- # Reemplazar los widgets desordenados de la vid=edition por tablas tipo any_rset #---- if role == "subject": subject_eid = eid target_eid = 'eid-objeto' rql = ('Any X,X WHERE X is %(target)s,Y is %(entity)s,' ' Y %(relate)s X, Y eid %(eid)s ' % { 'relate': rschema, 'target': target, 'entity': etype, 'eid': eid }) else: subject_eid = 'eid-objeto' target_eid = eid rql = ('Any X,X WHERE X is %(target)s,Y is %(entity)s,' ' X %(relate)s Y, Y eid %(eid)s' % { 'relate': rschema, 'target': target, 'entity': etype, 'eid': eid }) rset = form._cw.execute(rql) #---- # Incluir el activador ([x]) de borrado de relaciones en la tabla preentada. # + cellvid redf-del-rel para el activador de borrado # + cellvid pending_del para que las relaciones seleccionadas se marquen visualmente #---- pending_deletes = get_pending_deletes(form._cw, eid) ea_relation = rschema.type param_but = '%s|%s|%s|%s|%s' % ('Y', eid, ea_relation, role, str(pending_deletes)) kwargs = { 'ea_rel_entity': target, 'ea_relation': rschema, 'role': role, 'headers': (' '), 'cellvids': { 0: 'rdef-del-rel', '_0': param_but }, 'limit': 10, 'rdefkwargs': {}, 'title': u'no-widget-title', } w(form._cw.view('w-table', rset, 'null', **kwargs)) #---- w(u' </div>' # </div> de accordion-inner u'</div></div>' # </div> de accordion-body-collapse -- </div> accordion-group u'</div>') # </div> de span4 if w_num_rel == 3: # controlar el cambio de row de los widgets w(u'</div>') w_num_rel = 0 if w_num_rel > 0: w(u'</div>') w(u'</div>' u'</div>') # FIXME try to change this table with a fancy html code. pendings = list(field.restore_pending_inserts(form)) w(u'<table id="relatedEntities">') if not pendings: w(u'<tr><th> </th><td> </td></tr>') else: for row in pendings: # soon to be linked to entities w(u'<tr id="tr%s">' % row[1]) w(u'<th>%s</th>' % row[3]) w(u'<td>') w(u'<a class="handle" title="%s" href="%s">[x]</a>' % (_('cancel this insert'), row[2])) w(u'<a id="a%s" class="editionPending" href="%s">%s</a>' % (row[1], row[4], xml_escape(row[5]))) w(u'</td>') w(u'</tr>') w(u'<tr id="relationSelectorRow_%s" class="separator">' % eid) w(u'<th class="labelCol">') w(u'<select id="relationSelector_%s" tabindex="%s" ' u'onchange="javascript:showMatchingSelect' u'(this.options[this.selectedIndex].value,%s);">' % (eid, req.next_tabindex(), xml_escape(json_dumps(eid)))) w(u'<option value="">%s</option>' % _('select a relation')) for i18nrtype, rschema, role in field.relations: # more entities to link to w(u'<option value="%s_%s">%s</option>' % (rschema, role, i18nrtype)) w(u'</select>') w(u'</th>') w(u'<td id="unrelatedDivs_%s"></td>' % eid) w(u'</tr>') w(u'</table>') return '\n'.join(stream)
def guess_field(eschema, rschema, role='subject', req=None, **kwargs): """This function return the most adapted field to edit the given relation (`rschema`) where the given entity type (`eschema`) is the subject or object (`role`). The field is initialized according to information found in the schema, though any value can be explicitly specified using `kwargs`. """ fieldclass = None rdef = eschema.rdef(rschema, role) if role == 'subject': targetschema = rdef.object if rschema.final: if rdef.get('internationalizable'): kwargs.setdefault('internationalizable', True) else: targetschema = rdef.subject card = rdef.role_cardinality(role) composite = getattr(rdef, 'composite', None) kwargs['name'] = rschema.type kwargs['role'] = role kwargs['eidparam'] = True # don't mark composite relation as required, we want the composite element # to be removed when not linked to its parent kwargs.setdefault('required', card in '1+' and composite != neg_role(role)) if role == 'object': kwargs.setdefault('label', (eschema.type, rschema.type + '_object')) else: kwargs.setdefault('label', (eschema.type, rschema.type)) kwargs.setdefault('help', rdef.description) if rschema.final: fieldclass = kwargs.pop('fieldclass', FIELDS[targetschema]) if issubclass(fieldclass, FileField): if req: aff_kwargs = req.vreg['uicfg'].select('autoform_field_kwargs', req) else: aff_kwargs = _AFF_KWARGS for metadata in KNOWN_METAATTRIBUTES: metaschema = eschema.has_metadata(rschema, metadata) if metaschema is not None: metakwargs = aff_kwargs.etype_get(eschema, metaschema, 'subject') kwargs['%s_field' % metadata] = guess_field(eschema, metaschema, req=req, **metakwargs) elif issubclass(fieldclass, StringField): if eschema.has_metadata(rschema, 'format'): # use RichTextField instead of StringField if the attribute has # a "format" metadata. But getting information from constraints # may be useful anyway... for cstr in rdef.constraints: if isinstance(cstr, StaticVocabularyConstraint): raise Exception( 'rich text field with static vocabulary') return RichTextField(**kwargs) # init StringField parameters according to constraints for cstr in rdef.constraints: if isinstance(cstr, StaticVocabularyConstraint): kwargs.setdefault('choices', cstr.vocabulary) break for cstr in rdef.constraints: if isinstance(cstr, SizeConstraint) and cstr.max is not None: kwargs['max_length'] = cstr.max return fieldclass(**kwargs) else: fieldclass = kwargs.pop('fieldclass', RelationField) return fieldclass.fromcardinality(card, **kwargs)
def _compute_ttypes(self, rschema, role): dual_role = neg_role(role) return getattr(rschema, '%ss' % dual_role)()