# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr## This file is part of CubicWeb.## CubicWeb is free software: you can redistribute it and/or modify it under the# terms of the GNU Lesser General Public License as published by the Free# Software Foundation, either version 2.1 of the License, or (at your option)# any later version.## CubicWeb is distributed in the hope that it will be useful, but WITHOUT# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more# details.## You should have received a copy of the GNU Lesser General Public License along# with CubicWeb. If not, see <http://www.gnu.org/licenses/>."""Specific views for schema related entities"""__docformat__="restructuredtext en"_=unicodefromitertoolsimportcycleimporttempfileimportos,os.pathasospimportcodecsfromlogilab.common.graphimportGraphGenerator,DotBackendfromlogilab.common.ureportsimportSection,Tablefromlogilab.common.registryimportyesfromlogilab.mtconverterimportxml_escapefromyamsimportBASE_TYPES,schema2dotass2dfromyams.buildobjsimportDEFAULT_ATTRPERMSfromcubicweb.predicatesimport(is_instance,match_user_groups,match_kwargs,has_related_entities,authenticated_user)fromcubicweb.schemaimport(META_RTYPES,SCHEMA_TYPES,SYSTEM_RTYPES,WORKFLOW_TYPES,INTERNAL_TYPES)fromcubicweb.utilsimportmake_uidfromcubicweb.viewimportEntityView,StartupViewfromcubicwebimporttags,uilibfromcubicweb.webimportaction,facet,schemaviewerfromcubicweb.web.viewsimportuicfg,primary,baseviews,tabs,tableview,ibreadcrumbsALWAYS_SKIP_TYPES=BASE_TYPES|SCHEMA_TYPESSKIP_TYPES=(ALWAYS_SKIP_TYPES|META_RTYPES|SYSTEM_RTYPES|WORKFLOW_TYPES|INTERNAL_TYPES)SKIP_TYPES.update(set(('CWUser','CWGroup','EmailAddress','Bookmark')))defskip_types(req):ifint(req.form.get('skipmeta',True)):returnSKIP_TYPESreturnALWAYS_SKIP_TYPES_pvs=uicfg.primaryview_section_pvdc=uicfg.primaryview_display_ctrlfor_actionin('read','add','update','delete'):_pvs.tag_subject_of(('*','%s_permission'%_action,'*'),'hidden')_pvs.tag_object_of(('*','%s_permission'%_action,'*'),'hidden')for_etypein('CWEType','CWRType','CWAttribute','CWRelation'):_pvdc.tag_attribute((_etype,'description'),{'showlabel':False})_pvs.tag_attribute(('CWEType','name'),'hidden')_pvs.tag_attribute(('CWEType','final'),'hidden')_pvs.tag_object_of(('*','workflow_of','CWEType'),'hidden')_pvs.tag_subject_of(('CWEType','default_workflow','*'),'hidden')_pvs.tag_object_of(('*','specializes','CWEType'),'hidden')_pvs.tag_subject_of(('CWEType','specializes','*'),'hidden')_pvs.tag_object_of(('*','from_entity','CWEType'),'hidden')_pvs.tag_object_of(('*','to_entity','CWEType'),'hidden')_pvs.tag_attribute(('CWRType','name'),'hidden')_pvs.tag_attribute(('CWRType','final'),'hidden')_pvs.tag_object_of(('*','relation_type','CWRType'),'hidden')_pvs.tag_subject_of(('CWAttribute','constrained_by','*'),'hidden')_pvs.tag_subject_of(('CWRelation','constrained_by','*'),'hidden')classSecurityViewMixIn(object):"""mixin providing methods to display security information for a entity, relation or relation definition schema """defpermissions_table(self,erschema,permissions=None):self._cw.add_css('cubicweb.acl.css')w=self.w_=self._cw._w(u'<table class="listing schemaInfo">')w(u'<tr><th>%s</th><th>%s</th><th>%s</th></tr>'%(_("permission"),_('granted to groups'),_('rql expressions')))foractioninerschema.ACTIONS:w(u'<tr><td>%s</td><td>'%_(action))ifpermissionsisNone:groups=erschema.get_groups(action)rqlexprs=sorted(e.expressionforeinerschema.get_rqlexprs(action))else:groups=permissions[action][0]rqlexprs=permissions[action][1]# XXX get group entity and call it's incontext viewgroups=[u'<a class="%s" href="%s">%s</a>'%(group,self._cw.build_url('cwgroup/%s'%group),label)forlabel,groupinsorted((_(g),g)forgingroups)]w(u'<br/>'.join(groups))w(u'</td><td>')w(u'<br/>'.join(rqlexprs))w(u'</td></tr>\n')w(u'</table>')defgrouped_permissions_table(self,rschema):# group relation definitions with identical permissionsperms={}forrdefinrschema.rdefs.itervalues():rdef_perms=[]foractioninrdef.ACTIONS:groups=sorted(rdef.get_groups(action))exprs=sorted(e.expressionforeinrdef.get_rqlexprs(action))rdef_perms.append((action,(tuple(groups),tuple(exprs))))rdef_perms=tuple(rdef_perms)ifrdef_permsinperms:perms[rdef_perms].append((rdef.subject,rdef.object))else:perms[rdef_perms]=[(rdef.subject,rdef.object)]# set layout permissions in a table for each group of relation# definitionw=self.w_=self._cw._w(u'<div style="margin: 0px 1.5em">')tmpl=u'<strong>%s</strong> %s <strong>%s</strong>'forperm,rdefsinperms.iteritems():w(u'<div>%s</div>'%u', '.join(tmpl%(_(s.type),_(rschema.type),_(o.type))fors,oinrdefs))# accessing rdef from previous loop by design: only used to get# ACTIONSself.permissions_table(rdef,dict(perm))w(u'</div>')# global schema view ###########################################################classSchemaView(tabs.TabsMixin,StartupView):"""display schema information (graphically, listing tables...) in tabs"""__regid__='schema'title=_('data model schema')tabs=[_('schema-diagram'),_('schema-entity-types'),_('schema-relation-types')]default_tab='schema-diagram'defcall(self):self.w(u'<h1>%s</h1>'%self._cw._(self.title))self.render_tabs(self.tabs,self.default_tab)classSchemaImageTab(StartupView):__regid__='schema-diagram'defcall(self):_=self._cw._self.w(self._cw._(u'<div>This schema of the data model <em>excludes</em> the ''meta-data, but you can also display a <a href="%s">complete ''schema with meta-data</a>.</div>')%xml_escape(self._cw.build_url('view',vid='schemagraph',skipmeta=0)))self.w(u'<div><a href="%s">%s</a></div>'%(self._cw.build_url('view',vid='owl'),self._cw._(u'Download schema as OWL')))self.wview('schemagraph')classSchemaETypeTab(StartupView):__regid__='schema-entity-types'defcall(self):self.wview('table',self._cw.execute('Any X ORDERBY N WHERE X is CWEType, X name N, X final FALSE'))classSchemaRTypeTab(StartupView):__regid__='schema-relation-types'defcall(self):self.wview('table',self._cw.execute('Any X ORDERBY N WHERE X is CWRType, X name N, X final FALSE'))# CWEType ####################################################################### register msgid generated in entity relations tables_('i18ncard_1'),_('i18ncard_?'),_('i18ncard_+'),_('i18ncard_*')classCWETypePrimaryView(tabs.TabbedPrimaryView):__select__=is_instance('CWEType')tabs=[_('cwetype-description'),_('cwetype-box'),_('cwetype-workflow'),_('cwetype-views'),_('cwetype-permissions')]default_tab='cwetype-description'classCWETypeDescriptionTab(tabs.PrimaryTab):__regid__='cwetype-description'__select__=tabs.PrimaryTab.__select__&is_instance('CWEType')defrender_entity_attributes(self,entity):super(CWETypeDescriptionTab,self).render_entity_attributes(entity)_=self._cw._# inheritanceifentity.specializes:self.w(u'<div><strong>%s</strong>'%_('Parent class:'))self.wview('csv',entity.related('specializes','subject'))self.w(u'</div>')ifentity.reverse_specializes:self.w(u'<div><strong>%s</strong>'%_('Sub-classes:'))self.wview('csv',entity.related('specializes','object'))self.w(u'</div>')# entity schema imageself.wview('schemagraph',etype=entity.name)# entity schema attributesself.w(u'<h2>%s</h2>'%_('CWAttribute_plural'))rset=self._cw.execute('Any A,ON,D,C,A,DE,A, IDX,FTI,I18N,R,O,RN,S ORDERBY AA ''WHERE A is CWAttribute, A from_entity S, S eid %(x)s, ''A ordernum AA, A defaultval D, A description DE, A cardinality C, ''A fulltextindexed FTI, A internationalizable I18N, A indexed IDX, ''A relation_type R, R name RN, A to_entity O, O name ON',{'x':entity.eid})self.wview('table',rset,'null',cellvids={0:'rdef-name-cell',2:'etype-attr-defaultval-cell',3:'etype-attr-cardinality-cell',4:'rdef-constraints-cell',6:'rdef-options-cell'},headers=(_(u'name'),_(u'type'),_(u'default value'),_(u'required'),_(u'constraints'),_(u'description'),_('options')))# entity schema relationsself.w(u'<h2>%s</h2>'%_('CWRelation_plural'))cellvids={0:'rdef-name-cell',2:'etype-rel-cardinality-cell',3:'rdef-constraints-cell',4:'rdef-options-cell'}headers=[_(u'name'),_(u'object type'),_(u'cardinality'),_(u'constraints'),_(u'options')]rset=self._cw.execute('Any A,TT,"i18ncard_"+SUBSTRING(C,1,1),A,A, K,TTN,R,RN ORDERBY RN ''WHERE A is CWRelation, A from_entity S, S eid %(x)s, ''A composite K, A cardinality C, ''A relation_type R, R name RN, A to_entity TT, TT name TTN',{'x':entity.eid})ifrset:self.w(u'<h5>%s%s</h5>'%(entity.name,_('is subject of:')))self.wview('table',rset,cellvids=cellvids,headers=headers)rset=self._cw.execute('Any A,TT,"i18ncard_"+SUBSTRING(C,1,1),A,A, K,TTN,R,RN ORDERBY RN ''WHERE A is CWRelation, A to_entity O, O eid %(x)s, ''A composite K, A cardinality C, ''A relation_type R, R name RN, A from_entity TT, TT name TTN',{'x':entity.eid})ifrset:cellvids[0]='rdef-object-name-cell'headers[1]=_(u'subject type')self.w(u'<h5>%s%s</h5>'%(entity.name,_('is object of:')))self.wview('table',rset,cellvids=cellvids,headers=headers)classCWETypeAttributeCardinalityCell(baseviews.FinalView):__regid__='etype-attr-cardinality-cell'defcell_call(self,row,col):ifself.cw_rset.rows[row][col][0]=='1':self.w(self._cw._(u'yes'))else:self.w(self._cw._(u'no'))classCWETypeAttributeDefaultValCell(baseviews.FinalView):__regid__='etype-attr-defaultval-cell'defcell_call(self,row,col):defaultval=self.cw_rset.rows[row][col]ifdefaultvalisnotNone:self.w(unicode(self.cw_rset.rows[row][col].unzpickle()))classCWETypeRelationCardinalityCell(baseviews.FinalView):__regid__='etype-rel-cardinality-cell'defcell_call(self,row,col):self.w(self._cw._(self.cw_rset.rows[row][col]))classCWETypeBoxTab(EntityView):__regid__='cwetype-box'__select__=is_instance('CWEType')defcell_call(self,row,col):viewer=schemaviewer.SchemaViewer(self._cw)entity=self.cw_rset.get_entity(row,col)eschema=self._cw.vreg.schema.eschema(entity.name)layout=viewer.visit_entityschema(eschema)self.w(uilib.ureport_as_html(layout))self.w(u'<br class="clear"/>')classCWETypePermTab(SecurityViewMixIn,EntityView):__regid__='cwetype-permissions'__select__=is_instance('CWEType')&authenticated_user()defcell_call(self,row,col):entity=self.cw_rset.get_entity(row,col)eschema=self._cw.vreg.schema.eschema(entity.name)self.w(u'<h4>%s</h4>'%self._cw._('This entity type permissions:'))self.permissions_table(eschema)self.w(u'<div style="margin: 0px 1.5em">')self.w(u'<h4>%s</h4>'%self._cw._('Attributes permissions:'))forattr,etypeineschema.attribute_definitions():ifattrnotinMETA_RTYPES:rdef=eschema.rdef(attr)attrtype=str(rdef.rtype)self.w(u'<h4 class="schema">%s (%s)</h4> '%(attrtype,self._cw._(attrtype)))self.permissions_table(rdef)self.w(u'</div>')classCWETypeWorkflowTab(EntityView):__regid__='cwetype-workflow'__select__=(is_instance('CWEType')&has_related_entities('workflow_of','object'))defcell_call(self,row,col):entity=self.cw_rset.get_entity(row,col)ifentity.default_workflow:wf=entity.default_workflow[0]iflen(entity.reverse_workflow_of)>1:self.w(u'<h1>%s (%s)</h1>'%(wf.name,self._cw._('default_workflow')))self.display_workflow(wf)defaultwfeid=wf.eidelse:self.w(u'<div class="error">%s</div>'%self._cw._('There is no default workflow'))defaultwfeid=Noneforaltwfinentity.reverse_workflow_of:ifaltwf.eid==defaultwfeid:continueself.w(u'<h1>%s</h1>'%altwf.name)self.display_workflow(altwf)defdisplay_workflow(self,wf):self.w(wf.view('wfgraph'))self.w('<a href="%s">%s</a>'%(wf.absolute_url(),self._cw._('more info about this workflow')))classCWETypeViewsTab(EntityView):"""possible views for this entity type"""__regid__='cwetype-views'__select__=EntityView.__select__&is_instance('CWEType')defcell_call(self,row,col):entity=self.cw_rset.get_entity(row,col)_=self._cw._self.w('<div>%s</div>'%_('Non exhaustive list of views that may ''apply to entities of this type'))views=[(view.content_type,view.__regid__,_(view.title))forviewinself.possible_views(entity.name)]self.wview('pyvaltable',pyvalue=sorted(views),headers=(_(u'content type'),_(u'view identifier'),_(u'view title')))defpossible_views(self,etype):rset=self._cw.etype_rset(etype)return[vforvinself._cw.vreg['views'].possible_views(self._cw,rset)ifv.category!='startupview']classCWETypeOneLineView(baseviews.OneLineView):__select__=is_instance('CWEType')defcell_call(self,row,col,**kwargs):entity=self.cw_rset.get_entity(row,col)ifentity.final:self.w(u'<em class="finalentity">')super(CWETypeOneLineView,self).cell_call(row,col,**kwargs)ifentity.final:self.w(u'</em>')# CWRType ######################################################################classCWRTypePrimaryView(tabs.TabbedPrimaryView):__select__=is_instance('CWRType')tabs=[_('cwrtype-description'),_('cwrtype-permissions')]default_tab='cwrtype-description'classCWRTypeDescriptionTab(tabs.PrimaryTab):__regid__='cwrtype-description'__select__=is_instance('CWRType')defrender_entity_attributes(self,entity):super(CWRTypeDescriptionTab,self).render_entity_attributes(entity)_=self._cw._ifnotentity.final:self.wview('schemagraph',rtype=entity.name)rset=self._cw.execute('Any R,C,R,R, RT WHERE ''R relation_type RT, RT eid %(x)s, ''R cardinality C',{'x':entity.eid})self.wview('table',rset,'null',headers=(_(u'relation'),_(u'cardinality'),_(u'constraints'),_(u'options')),cellvids={2:'rdef-constraints-cell',3:'rdef-options-cell'})classCWRTypePermTab(SecurityViewMixIn,EntityView):__regid__='cwrtype-permissions'__select__=is_instance('CWRType')&authenticated_user()defcell_call(self,row,col):entity=self.cw_rset.get_entity(row,col)rschema=self._cw.vreg.schema.rschema(entity.name)self.grouped_permissions_table(rschema)# CWAttribute / CWRelation #####################################################classRDEFPrimaryView(tabs.TabbedPrimaryView):__select__=is_instance('CWRelation','CWAttribute')tabs=[_('rdef-description'),_('rdef-permissions')]default_tab='rdef-description'classRDEFDescriptionTab(tabs.PrimaryTab):__regid__='rdef-description'__select__=is_instance('CWRelation','CWAttribute')defrender_entity_attributes(self,entity):super(RDEFDescriptionTab,self).render_entity_attributes(entity)rdef=entity.yams_schema()ifrdef.constraints:self.w(u'<h4>%s</h4>'%self._cw._('constrained_by'))self.w(entity.view('rdef-constraints-cell'))classRDEFPermTab(SecurityViewMixIn,EntityView):__regid__='rdef-permissions'__select__=is_instance('CWRelation','CWAttribute')&authenticated_user()defcell_call(self,row,col):self.permissions_table(self.cw_rset.get_entity(row,col).yams_schema())classRDEFNameView(tableview.CellView):"""display relation name and its translation only in a cell view, link to relation definition's primary view (for use in entity type relations table for instance) """__regid__='rdef-name-cell'__select__=is_instance('CWRelation','CWAttribute')defcell_call(self,row,col):entity=self.cw_rset.get_entity(row,col)rtype=entity.relation_type[0].name# XXX use context entity + pgettextself.w(u'<a href="%s">%s</a> (%s)'%(entity.absolute_url(),rtype,self._cw._(rtype)))classRDEFObjectNameView(tableview.CellView):"""same as RDEFNameView but when the context is the object entity """__regid__='rdef-object-name-cell'__select__=is_instance('CWRelation','CWAttribute')defcell_call(self,row,col):entity=self.cw_rset.get_entity(row,col)rtype=entity.relation_type[0].name# XXX use context entity + pgettextself.w(u'<a href="%s">%s</a> (%s)'%(entity.absolute_url(),rtype,self._cw.__(rtype+'_object')))classRDEFConstraintsCell(EntityView):__regid__='rdef-constraints-cell'__select__=is_instance('CWAttribute','CWRelation')defcell_call(self,row,col):entity=self.cw_rset.get_entity(row,col)rschema=self._cw.vreg.schema.rschema(entity.rtype.name)rdef=rschema.rdefs[(entity.stype.name,entity.otype.name)]constraints=[xml_escape(unicode(c))forcingetattr(rdef,'constraints')]self.w(u'<br/>'.join(constraints))classCWAttributeOptionsCell(EntityView):__regid__='rdef-options-cell'__select__=is_instance('CWAttribute')defcell_call(self,row,col):entity=self.cw_rset.get_entity(row,col)options=[]ifentity.indexed:options.append(self._cw._('indexed'))ifentity.fulltextindexed:options.append(self._cw._('fulltextindexed'))ifentity.internationalizable:options.append(self._cw._('internationalizable'))self.w(u','.join(options))classCWRelationOptionsCell(EntityView):__regid__='rdef-options-cell'__select__=is_instance('CWRelation',)defcell_call(self,row,col):entity=self.cw_rset.get_entity(row,col)rtype=entity.rtypeoptions=[]ifrtype.symmetric:options.append(self._cw._('symmetric'))ifrtype.inlined:options.append(self._cw._('inlined'))ifrtype.fulltext_container:options.append('%s=%s'%(self._cw._('fulltext_container'),self._cw._(rtype.fulltext_container)))ifentity.composite:options.append('%s=%s'%(self._cw._('composite'),self._cw._(entity.composite)))self.w(u','.join(options))# schema images ###############################################################classRestrictedSchemaVisitorMixIn(object):def__init__(self,req,*args,**kwargs):self._cw=reqsuper(RestrictedSchemaVisitorMixIn,self).__init__(*args,**kwargs)defshould_display_schema(self,rschema):return(super(RestrictedSchemaVisitorMixIn,self).should_display_schema(rschema)andrschema.may_have_permission('read',self._cw))defshould_display_attr(self,eschema,rschema):return(super(RestrictedSchemaVisitorMixIn,self).should_display_attr(eschema,rschema)andeschema.rdef(rschema).may_have_permission('read',self._cw))classFullSchemaVisitor(RestrictedSchemaVisitorMixIn,s2d.FullSchemaVisitor):passclassOneHopESchemaVisitor(RestrictedSchemaVisitorMixIn,s2d.OneHopESchemaVisitor):passclassOneHopRSchemaVisitor(RestrictedSchemaVisitorMixIn,s2d.OneHopRSchemaVisitor):passclassCWSchemaDotPropsHandler(s2d.SchemaDotPropsHandler):def__init__(self,visitor,cw):self.visitor=visitorself.cw=cwself.nextcolor=cycle(('#ff7700','#000000','#ebbc69','#888888')).nextself.colors={}defnode_properties(self,eschema):"""return DOT drawing options for an entity schema include href"""label=['{',eschema.type,'|']label.append(r'\l'.join('%s (%s)'%(rel.type,eschema.rdef(rel.type).object)forrelineschema.ordered_relations()ifrel.finalandself.visitor.should_display_attr(eschema,rel)))label.append(r'\l}')# trailing \l ensure alignement of the last onereturn{'label':''.join(label),'shape':"record",'fontname':"Courier",'style':"filled",'href':self.cw.build_url('cwetype/%s'%eschema.type),'fontsize':'10px'}defedge_properties(self,rschema,subjnode,objnode):"""return default DOT drawing options for a relation schema"""# Inheritance relation (i.e 'specializes').ifrschemaisNone:kwargs={'label':'Parent class','color':'grey','style':'filled','arrowhead':'empty','fontsize':'10px'}# symmetric rels are handled differently, let yams decide what's bestelifrschema.symmetric:kwargs={'label':rschema.type,'color':'#887788','style':'dashed','dir':'both','arrowhead':'normal','arrowtail':'normal','fontsize':'10px','href':self.cw.build_url('cwrtype/%s'%rschema.type)}else:kwargs={'label':rschema.type,'color':'black','style':'filled','fontsize':'10px','href':self.cw.build_url('cwrtype/%s'%rschema.type)}rdef=rschema.rdef(subjnode,objnode)composite=rdef.compositeifrdef.composite=='subject':kwargs['arrowhead']='none'kwargs['arrowtail']='diamond'elifrdef.composite=='object':kwargs['arrowhead']='diamond'kwargs['arrowtail']='none'else:kwargs['arrowhead']='open'kwargs['arrowtail']='none'# UML like cardinalities notation, omitting 1..1ifrdef.cardinality[1]!='1':kwargs['taillabel']=s2d.CARD_MAP[rdef.cardinality[1]]ifrdef.cardinality[0]!='1':kwargs['headlabel']=s2d.CARD_MAP[rdef.cardinality[0]]try:kwargs['color']=self.colors[rschema]exceptKeyError:kwargs['color']=self.nextcolor()self.colors[rschema]=kwargs['color']kwargs['fontcolor']=kwargs['color']# dot label decoration is just awful (1 line underlining the label# + 1 line going to the closest edge spline point)kwargs['decorate']='false'#kwargs['labelfloat'] = 'true'returnkwargsclassSchemaGraphView(StartupView):__regid__='schemagraph'defcall(self,etype=None,rtype=None,alt=''):if'MSIE 8'inself._cw.useragent():returnschema=self._cw.vreg.schemaifetype:assertrtypeisNonevisitor=OneHopESchemaVisitor(self._cw,schema.eschema(etype),skiptypes=skip_types(self._cw))alt=self._cw._('graphical representation of the %(etype)s ''entity type from %(appid)s data model')elifrtype:visitor=OneHopRSchemaVisitor(self._cw,schema.rschema(rtype),skiptypes=skip_types(self._cw))alt=self._cw._('graphical representation of the %(rtype)s ''relation type from %(appid)s data model')else:visitor=FullSchemaVisitor(self._cw,schema,skiptypes=skip_types(self._cw))alt=self._cw._('graphical representation of %(appid)s data model')alt%={'rtype':rtype,'etype':etype,'appid':self._cw.vreg.config.appid}prophdlr=CWSchemaDotPropsHandler(visitor,self._cw)generator=GraphGenerator(DotBackend('schema','BT',ratio='compress',size=None,renderer='dot',additionnal_param={'overlap':'false','splines':'true','sep':'0.2',}))# svg image filefd,tmpfile=tempfile.mkstemp('.svg')os.close(fd)generator.generate(visitor,prophdlr,tmpfile)withcodecs.open(tmpfile,'rb',encoding='utf-8')assvgfile:self.w(svgfile.read())# breadcrumbs ##################################################################classCWRelationIBreadCrumbsAdapter(ibreadcrumbs.IBreadCrumbsAdapter):__select__=is_instance('CWRelation')defparent_entity(self):returnself.entity.rtypeclassCWAttributeIBreadCrumbsAdapter(ibreadcrumbs.IBreadCrumbsAdapter):__select__=is_instance('CWAttribute')defparent_entity(self):returnself.entity.stypeclassCWConstraintIBreadCrumbsAdapter(ibreadcrumbs.IBreadCrumbsAdapter):__select__=is_instance('CWConstraint')defparent_entity(self):ifself.entity.reverse_constrained_by:returnself.entity.reverse_constrained_by[0]classRQLExpressionIBreadCrumbsAdapter(ibreadcrumbs.IBreadCrumbsAdapter):__select__=is_instance('RQLExpression')defparent_entity(self):returnself.entity.expression_of# misc: facets, actions ########################################################classCWFinalFacet(facet.AttributeFacet):__regid__='cwfinal-facet'__select__=facet.AttributeFacet.__select__&is_instance('CWEType','CWRType')rtype='final'classViewSchemaAction(action.Action):__regid__='schema'__select__=yes()title=_('data model schema')order=30category='manage'defurl(self):returnself._cw.build_url(self.__regid__)