diff -r 058bb3dc685f -r 0b59724cb3f2 web/views/schema.py --- a/web/views/schema.py Mon Jan 04 18:40:30 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,712 +0,0 @@ -# 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 . -"""Specific views for schema related entities""" - -__docformat__ = "restructuredtext en" -from cubicweb import _ - -from itertools import cycle - -import tempfile -import os, os.path as osp -import codecs - -from six import text_type - -from logilab.common.graph import GraphGenerator, DotBackend -from logilab.common.ureports import Section, Table -from logilab.common.registry import yes -from logilab.mtconverter import xml_escape -from yams import BASE_TYPES, schema2dot as s2d -from yams.buildobjs import DEFAULT_ATTRPERMS - -from cubicweb.predicates import (is_instance, match_user_groups, match_kwargs, - has_related_entities, authenticated_user) -from cubicweb.schema import (META_RTYPES, SCHEMA_TYPES, SYSTEM_RTYPES, - WORKFLOW_TYPES, INTERNAL_TYPES) -from cubicweb.utils import make_uid -from cubicweb.view import EntityView, StartupView -from cubicweb import tags, uilib -from cubicweb.web import action, facet, schemaviewer -from cubicweb.web.views import uicfg, primary, baseviews, tabs, tableview, ibreadcrumbs - -ALWAYS_SKIP_TYPES = BASE_TYPES | SCHEMA_TYPES -SKIP_TYPES = (ALWAYS_SKIP_TYPES | META_RTYPES | SYSTEM_RTYPES | WORKFLOW_TYPES - | INTERNAL_TYPES) -SKIP_TYPES.update(set(('CWUser', 'CWGroup', 'EmailAddress', 'Bookmark'))) - -def skip_types(req): - if int(req.form.get('skipmeta', True)): - return SKIP_TYPES - return ALWAYS_SKIP_TYPES - -_pvs = uicfg.primaryview_section -_pvdc = uicfg.primaryview_display_ctrl - -for _action in ('read', 'add', 'update', 'delete'): - _pvs.tag_subject_of(('*', '%s_permission' % _action, '*'), 'hidden') - _pvs.tag_object_of(('*', '%s_permission' % _action, '*'), 'hidden') - -for _etype in ('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') - - -class SecurityViewMixIn(object): - """mixin providing methods to display security information for a entity, - relation or relation definition schema - """ - cssclass = "listing schemaInfo" - - def permissions_table(self, erschema, permissions=None): - self._cw.add_css('cubicweb.acl.css') - w = self.w - _ = self._cw._ - w(u'' % self.cssclass) - w(u'' % ( - _("permission"), _('granted to groups'), _('rql expressions'))) - for action in erschema.ACTIONS: - w(u'\n') - w(u'
%s%s%s
%s' % _(action)) - if permissions is None: - groups = erschema.get_groups(action) - rqlexprs = sorted(e.expression for e in erschema.get_rqlexprs(action)) - else: - groups = permissions[action][0] - rqlexprs = permissions[action][1] - # XXX get group entity and call it's incontext view - groups = [u'%s' % ( - group, self._cw.build_url('cwgroup/%s' % group), label) - for label, group in sorted((_(g), g) for g in groups)] - w(u'
'.join(groups)) - w(u'
') - w(u'
'.join(rqlexprs)) - w(u'
') - - def grouped_permissions_table(self, rschema): - # group relation definitions with identical permissions - perms = {} - for rdef in rschema.rdefs.values(): - rdef_perms = [] - for action in rdef.ACTIONS: - groups = sorted(rdef.get_groups(action)) - exprs = sorted(e.expression for e in rdef.get_rqlexprs(action)) - rdef_perms.append( (action, (tuple(groups), tuple(exprs))) ) - rdef_perms = tuple(rdef_perms) - if rdef_perms in perms: - 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 - # definition - w = self.w - _ = self._cw._ - w(u'
') - tmpl = u'%s %s %s' - for perm, rdefs in perms.items(): - w(u'
%s
' % u', '.join( - tmpl % (_(s.type), _(rschema.type), _(o.type)) for s, o in rdefs)) - # accessing rdef from previous loop by design: only used to get - # ACTIONS - self.permissions_table(rdef, dict(perm)) - w(u'
') - - -# global schema view ########################################################### - -class SchemaView(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' - - def call(self): - self.w(u'

%s

' % self._cw._(self.title)) - self.render_tabs(self.tabs, self.default_tab) - - -class SchemaImageTab(StartupView): - __regid__ = 'schema-diagram' - - def call(self): - _ = self._cw._ - self.w(self._cw._( - u'
This schema of the data model excludes the ' - 'meta-data, but you can also display a complete ' - 'schema with meta-data.
') - % xml_escape(self._cw.build_url('view', vid='schemagraph', skipmeta=0))) - self.w(u'
%s
' % - (self._cw.build_url('view', vid='owl'), - self._cw._(u'Download schema as OWL'))) - self.wview('schemagraph') - -class SchemaETypeTab(StartupView): - __regid__ = 'schema-entity-types' - - def call(self): - self.wview('table', self._cw.execute( - 'Any X ORDERBY N WHERE X is CWEType, X name N, X final FALSE')) - - -class SchemaRTypeTab(StartupView): - __regid__ = 'schema-relation-types' - - def call(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_*') - -class CWETypePrimaryView(tabs.TabbedPrimaryView): - __select__ = is_instance('CWEType') - tabs = [_('cwetype-description'), _('cwetype-box'), _('cwetype-workflow'), - _('cwetype-views'), _('cwetype-permissions')] - default_tab = 'cwetype-description' - - -class CWETypeDescriptionTab(tabs.PrimaryTab): - __regid__ = 'cwetype-description' - __select__ = tabs.PrimaryTab.__select__ & is_instance('CWEType') - - def render_entity_attributes(self, entity): - super(CWETypeDescriptionTab, self).render_entity_attributes(entity) - _ = self._cw._ - # inheritance - if entity.specializes: - self.w(u'
%s' % _('Parent class:')) - self.wview('csv', entity.related('specializes', 'subject')) - self.w(u'
') - if entity.reverse_specializes: - self.w(u'
%s' % _('Sub-classes:')) - self.wview('csv', entity.related('specializes', 'object')) - self.w(u'
') - # entity schema image - self.wview('schemagraph', etype=entity.name) - # entity schema attributes - self.w(u'

%s

' % _('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 relations - self.w(u'

%s

' % _('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}) - if rset: - self.w(u'
%s %s
' % (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}) - if rset: - cellvids[0] = 'rdef-object-name-cell' - headers[1] = _(u'subject type') - self.w(u'
%s %s
' % (entity.name, _('is object of:'))) - self.wview('table', rset, cellvids=cellvids, headers=headers) - - -class CWETypeAttributeCardinalityCell(baseviews.FinalView): - __regid__ = 'etype-attr-cardinality-cell' - - def cell_call(self, row, col): - if self.cw_rset.rows[row][col][0] == '1': - self.w(self._cw._(u'yes')) - else: - self.w(self._cw._(u'no')) - - -class CWETypeAttributeDefaultValCell(baseviews.FinalView): - __regid__ = 'etype-attr-defaultval-cell' - - def cell_call(self, row, col): - defaultval = self.cw_rset.rows[row][col] - if defaultval is not None: - self.w(text_type(self.cw_rset.rows[row][col].unzpickle())) - -class CWETypeRelationCardinalityCell(baseviews.FinalView): - __regid__ = 'etype-rel-cardinality-cell' - - def cell_call(self, row, col): - self.w(self._cw._(self.cw_rset.rows[row][col])) - - -class CWETypeBoxTab(EntityView): - __regid__ = 'cwetype-box' - __select__ = is_instance('CWEType') - - def cell_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'
') - - -class CWETypePermTab(SecurityViewMixIn, EntityView): - __regid__ = 'cwetype-permissions' - __select__ = is_instance('CWEType') & authenticated_user() - - def cell_call(self, row, col): - entity = self.cw_rset.get_entity(row, col) - eschema = self._cw.vreg.schema.eschema(entity.name) - self.w(u'

%s

' % self._cw._('This entity type permissions:')) - self.permissions_table(eschema) - self.w(u'
') - self.w(u'

%s

' % self._cw._('Attributes permissions:')) - for attr, etype in eschema.attribute_definitions(): - if attr not in META_RTYPES: - rdef = eschema.rdef(attr) - attrtype = str(rdef.rtype) - self.w(u'

%s (%s)

' - % (attrtype, self._cw._(attrtype))) - self.permissions_table(rdef) - self.w(u'
') - - -class CWETypeWorkflowTab(EntityView): - __regid__ = 'cwetype-workflow' - __select__ = (is_instance('CWEType') - & has_related_entities('workflow_of', 'object')) - - def cell_call(self, row, col): - entity = self.cw_rset.get_entity(row, col) - if entity.default_workflow: - wf = entity.default_workflow[0] - if len(entity.reverse_workflow_of) > 1: - self.w(u'

%s (%s)

' - % (wf.name, self._cw._('default_workflow'))) - self.display_workflow(wf) - defaultwfeid = wf.eid - else: - self.w(u'
%s
' - % self._cw._('There is no default workflow')) - defaultwfeid = None - for altwf in entity.reverse_workflow_of: - if altwf.eid == defaultwfeid: - continue - self.w(u'

%s

' % altwf.name) - self.display_workflow(altwf) - - def display_workflow(self, wf): - self.w(wf.view('wfgraph')) - self.w('%s' % ( - wf.absolute_url(), self._cw._('more info about this workflow'))) - - -class CWETypeViewsTab(EntityView): - """possible views for this entity type""" - __regid__ = 'cwetype-views' - __select__ = EntityView.__select__ & is_instance('CWEType') - - def cell_call(self, row, col): - entity = self.cw_rset.get_entity(row, col) - _ = self._cw._ - self.w('
%s
' % _('Non exhaustive list of views that may ' - 'apply to entities of this type')) - views = [(view.content_type, view.__regid__, _(view.title)) - for view in self.possible_views(entity.name)] - self.wview('pyvaltable', pyvalue=sorted(views), - headers=(_(u'content type'), _(u'view identifier'), - _(u'view title'))) - - def possible_views(self, etype): - rset = self._cw.etype_rset(etype) - return [v for v in self._cw.vreg['views'].possible_views(self._cw, rset) - if v.category != 'startupview'] - - -class CWETypeOneLineView(baseviews.OneLineView): - __select__ = is_instance('CWEType') - - def cell_call(self, row, col, **kwargs): - entity = self.cw_rset.get_entity(row, col) - if entity.final: - self.w(u'') - super(CWETypeOneLineView, self).cell_call(row, col, **kwargs) - if entity.final: - self.w(u'') - - -# CWRType ###################################################################### - -class CWRTypePrimaryView(tabs.TabbedPrimaryView): - __select__ = is_instance('CWRType') - tabs = [_('cwrtype-description'), _('cwrtype-permissions')] - default_tab = 'cwrtype-description' - - -class CWRTypeDescriptionTab(tabs.PrimaryTab): - __regid__ = 'cwrtype-description' - __select__ = is_instance('CWRType') - - def render_entity_attributes(self, entity): - super(CWRTypeDescriptionTab, self).render_entity_attributes(entity) - _ = self._cw._ - if not entity.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'}) - - -class CWRTypePermTab(SecurityViewMixIn, EntityView): - __regid__ = 'cwrtype-permissions' - __select__ = is_instance('CWRType') & authenticated_user() - - def cell_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 ##################################################### - -class RDEFPrimaryView(tabs.TabbedPrimaryView): - __select__ = is_instance('CWRelation', 'CWAttribute') - tabs = [_('rdef-description'), _('rdef-permissions')] - default_tab = 'rdef-description' - - -class RDEFDescriptionTab(tabs.PrimaryTab): - __regid__ = 'rdef-description' - __select__ = is_instance('CWRelation', 'CWAttribute') - - def render_entity_attributes(self, entity): - super(RDEFDescriptionTab, self).render_entity_attributes(entity) - rdef = entity.yams_schema() - if rdef.constraints: - self.w(u'

%s

' % self._cw._('constrained_by')) - self.w(entity.view('rdef-constraints-cell')) - - -class RDEFPermTab(SecurityViewMixIn, EntityView): - __regid__ = 'rdef-permissions' - __select__ = is_instance('CWRelation', 'CWAttribute') & authenticated_user() - - def cell_call(self, row, col): - self.permissions_table(self.cw_rset.get_entity(row, col).yams_schema()) - - -class RDEFNameView(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') - - def cell_call(self, row, col): - entity = self.cw_rset.get_entity(row, col) - rtype = entity.relation_type[0].name - # XXX use context entity + pgettext - self.w(u'%s (%s)' % ( - entity.absolute_url(), rtype, self._cw._(rtype))) - -class RDEFObjectNameView(tableview.CellView): - """same as RDEFNameView but when the context is the object entity - """ - __regid__ = 'rdef-object-name-cell' - __select__ = is_instance('CWRelation', 'CWAttribute') - - def cell_call(self, row, col): - entity = self.cw_rset.get_entity(row, col) - rtype = entity.relation_type[0].name - # XXX use context entity + pgettext - self.w(u'%s (%s)' % ( - entity.absolute_url(), rtype, self._cw.__(rtype + '_object'))) - -class RDEFConstraintsCell(EntityView): - __regid__ = 'rdef-constraints-cell' - __select__ = is_instance('CWAttribute', 'CWRelation') - - def cell_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(text_type(c)) for c in getattr(rdef, 'constraints')] - self.w(u'
'.join(constraints)) - -class CWAttributeOptionsCell(EntityView): - __regid__ = 'rdef-options-cell' - __select__ = is_instance('CWAttribute') - - def cell_call(self, row, col): - entity = self.cw_rset.get_entity(row, col) - options = [] - if entity.indexed: - options.append(self._cw._('indexed')) - if entity.fulltextindexed: - options.append(self._cw._('fulltextindexed')) - if entity.internationalizable: - options.append(self._cw._('internationalizable')) - self.w(u','.join(options)) - -class CWRelationOptionsCell(EntityView): - __regid__ = 'rdef-options-cell' - __select__ = is_instance('CWRelation',) - - def cell_call(self, row, col): - entity = self.cw_rset.get_entity(row, col) - rtype = entity.rtype - options = [] - if rtype.symmetric: - options.append(self._cw._('symmetric')) - if rtype.inlined: - options.append(self._cw._('inlined')) - if rtype.fulltext_container: - options.append('%s=%s' % (self._cw._('fulltext_container'), - self._cw._(rtype.fulltext_container))) - if entity.composite: - options.append('%s=%s' % (self._cw._('composite'), - self._cw._(entity.composite))) - self.w(u','.join(options)) - - -# schema images ############################################################### - -class RestrictedSchemaVisitorMixIn(object): - def __init__(self, req, *args, **kwargs): - self._cw = req - super(RestrictedSchemaVisitorMixIn, self).__init__(*args, **kwargs) - - def should_display_schema(self, rschema): - return (super(RestrictedSchemaVisitorMixIn, self).should_display_schema(rschema) - and rschema.may_have_permission('read', self._cw)) - - def should_display_attr(self, eschema, rschema): - return (super(RestrictedSchemaVisitorMixIn, self).should_display_attr(eschema, rschema) - and eschema.rdef(rschema).may_have_permission('read', self._cw)) - - -class FullSchemaVisitor(RestrictedSchemaVisitorMixIn, s2d.FullSchemaVisitor): - pass - -class OneHopESchemaVisitor(RestrictedSchemaVisitorMixIn, - s2d.OneHopESchemaVisitor): - pass - -class OneHopRSchemaVisitor(RestrictedSchemaVisitorMixIn, - s2d.OneHopRSchemaVisitor): - pass - -class CWSchemaDotPropsHandler(s2d.SchemaDotPropsHandler): - def __init__(self, visitor, cw): - self.visitor = visitor - self.cw = cw - self._cycle = iter(cycle(('#ff7700', '#000000', '#ebbc69', '#888888'))) - self.nextcolor = lambda: next(self._cycle) - - self.colors = {} - - def node_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) - for rel in eschema.ordered_relations() - if rel.final and self.visitor.should_display_attr(eschema, rel))) - label.append(r'\l}') # trailing \l ensure alignement of the last one - return {'label' : ''.join(label), 'shape' : "record", - 'fontname' : "Courier", 'style' : "filled", - 'href': self.cw.build_url('cwetype/%s' % eschema.type), - 'fontsize': '10px' - } - - def edge_properties(self, rschema, subjnode, objnode): - """return default DOT drawing options for a relation schema""" - # Inheritance relation (i.e 'specializes'). - if rschema is None: - kwargs = {'label': 'Parent class', - 'color' : 'grey', 'style' : 'filled', - 'arrowhead': 'empty', - 'fontsize': '10px'} - # symmetric rels are handled differently, let yams decide what's best - elif rschema.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.composite - if rdef.composite == 'subject': - kwargs['arrowhead'] = 'none' - kwargs['arrowtail'] = 'diamond' - elif rdef.composite == 'object': - kwargs['arrowhead'] = 'diamond' - kwargs['arrowtail'] = 'none' - else: - kwargs['arrowhead'] = 'open' - kwargs['arrowtail'] = 'none' - # UML like cardinalities notation, omitting 1..1 - if rdef.cardinality[1] != '1': - kwargs['taillabel'] = s2d.CARD_MAP[rdef.cardinality[1]] - if rdef.cardinality[0] != '1': - kwargs['headlabel'] = s2d.CARD_MAP[rdef.cardinality[0]] - try: - kwargs['color'] = self.colors[rschema] - except KeyError: - 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' - return kwargs - - -class SchemaGraphView(StartupView): - __regid__ = 'schemagraph' - - def call(self, etype=None, rtype=None, alt=''): - if 'MSIE 8' in self._cw.useragent(): - return - schema = self._cw.vreg.schema - if etype: - assert rtype is None - visitor = 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') - elif rtype: - 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 file - fd, tmpfile = tempfile.mkstemp('.svg') - try: - os.close(fd) - generator.generate(visitor, prophdlr, tmpfile) - with codecs.open(tmpfile, 'rb', encoding='utf-8') as svgfile: - self.w(svgfile.read()) - finally: - os.unlink(tmpfile) - -# breadcrumbs ################################################################## - -class CWRelationIBreadCrumbsAdapter(ibreadcrumbs.IBreadCrumbsAdapter): - __select__ = is_instance('CWRelation') - def parent_entity(self): - return self.entity.rtype - -class CWAttributeIBreadCrumbsAdapter(ibreadcrumbs.IBreadCrumbsAdapter): - __select__ = is_instance('CWAttribute') - def parent_entity(self): - return self.entity.stype - -class CWConstraintIBreadCrumbsAdapter(ibreadcrumbs.IBreadCrumbsAdapter): - __select__ = is_instance('CWConstraint') - def parent_entity(self): - if self.entity.reverse_constrained_by: - return self.entity.reverse_constrained_by[0] - -class RQLExpressionIBreadCrumbsAdapter(ibreadcrumbs.IBreadCrumbsAdapter): - __select__ = is_instance('RQLExpression') - def parent_entity(self): - return self.entity.expression_of - - -# misc: facets, actions ######################################################## - -class CWFinalFacet(facet.AttributeFacet): - __regid__ = 'cwfinal-facet' - __select__ = facet.AttributeFacet.__select__ & is_instance('CWEType', 'CWRType') - rtype = 'final' - - -class ViewSchemaAction(action.Action): - __regid__ = 'schema' - __select__ = yes() - - title = _('data model schema') - order = 30 - category = 'manage' - - def url(self): - return self._cw.build_url(self.__regid__)