web/views/primary.py
branchtls-sprint
changeset 1554 3a3263df6cdd
parent 1516 288d55a7c5e2
child 1558 f63d090eb34a
equal deleted inserted replaced
1553:3f91ef2397d0 1554:3a3263df6cdd
     2 
     2 
     3 :organization: Logilab
     3 :organization: Logilab
     4 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     4 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     6 """
     6 """
       
     7 __docformat__ = "restructuredtext en"
     7 
     8 
     8 from warnings import warn
     9 from warnings import warn
       
    10 
       
    11 from logilab.mtconverter import html_escape
     9 
    12 
    10 from cubicweb import Unauthorized
    13 from cubicweb import Unauthorized
    11 from cubicweb.view import EntityView
    14 from cubicweb.view import EntityView
       
    15 from cubicweb.web.uicfg import rdisplay
    12 
    16 
    13 _ = unicode
    17 _ = unicode
    14 
    18 
    15 PRIMARY_SKIP_RELS = set(['is', 'is_instance_of', 'identity',
       
    16                          'owned_by', 'created_by',
       
    17                          'in_state', 'wf_info_for', 'require_permission',
       
    18                          'from_entity', 'to_entity',
       
    19                          'see_also'])
       
    20 
    19 
    21 class PrimaryView(EntityView):
    20 class PrimaryView(EntityView):
    22     """the full view of an non final entity"""
    21     """the full view of an non final entity"""
    23     id = 'primary'
    22     id = 'primary'
    24     title = _('primary')
    23     title = _('primary')
    25     show_attr_label = True
    24     show_attr_label = True
    26     show_rel_label = True
    25     show_rel_label = True
    27     skip_none = True
    26     skip_none = True
    28     skip_attrs = ('eid', 'creation_date', 'modification_date')
    27     rdisplay = rdisplay
    29     skip_rels = ()
       
    30     main_related_section = True
    28     main_related_section = True
       
    29 
       
    30     @classmethod
       
    31     def vreg_initialization_completed(cls):
       
    32         """set default category tags for relations where it's not yet defined in
       
    33         the category relation tags
       
    34         """
       
    35         for eschema in cls.schema.entities():
       
    36             for rschema, tschemas, role in eschema.relation_definitions(True):
       
    37                 for tschema in tschemas:
       
    38                     if role == 'subject':
       
    39                         X, Y = eschema, tschema
       
    40                         card = rschema.rproperty(X, Y, 'cardinality')[0]
       
    41                         composed = rschema.rproperty(X, Y, 'composite') == 'object'
       
    42                     else:
       
    43                         X, Y = tschema, eschema
       
    44                         card = rschema.rproperty(X, Y, 'cardinality')[1]
       
    45                         composed = rschema.rproperty(X, Y, 'composite') == 'subject'
       
    46                     displayinfo = cls.rdisplay.get(rschema, role, X, Y)
       
    47                     if displayinfo is None:
       
    48                         if rschema.is_final():
       
    49                             if rschema.meta or tschema.type in ('Password', 'Bytes'):
       
    50                                 where = None
       
    51                             else:
       
    52                                 where = 'attributes'
       
    53                         elif card in '1+':
       
    54                             where = 'attributes'
       
    55                         elif composed:
       
    56                             where = 'relations'
       
    57                         else:
       
    58                             where = 'sideboxes'
       
    59                         displayinfo = {'where': where,
       
    60                                        'order': cls.rdisplay.get_timestamp()}
       
    61                         cls.rdisplay.tag_relation(displayinfo, (X, rschema, Y),
       
    62                                                   role)
       
    63                     displayinfo.setdefault('label', '%s_%s' % (rschema, role))
    31 
    64 
    32     def html_headers(self):
    65     def html_headers(self):
    33         """return a list of html headers (eg something to be inserted between
    66         """return a list of html headers (eg something to be inserted between
    34         <head> and </head> of the returned page
    67         <head> and </head> of the returned page
    35 
    68 
    39 
    72 
    40     def cell_call(self, row, col):
    73     def cell_call(self, row, col):
    41         self.row = row
    74         self.row = row
    42         # XXX move render_entity implementation here
    75         # XXX move render_entity implementation here
    43         self.render_entity(self.complete_entity(row, col))
    76         self.render_entity(self.complete_entity(row, col))
       
    77         self.maxrelated = self.req.property_value('navigation.related-limit')
    44 
    78 
    45     def render_entity(self, entity):
    79     def render_entity(self, entity):
    46         """return html to display the given entity"""
    80         """return html to display the given entity"""
    47         self.render_entity_title(entity)
    81         self.render_entity_title(entity)
    48         self.render_entity_metadata(entity)
    82         self.render_entity_metadata(entity)
    49         # entity's attributes and relations, excluding meta data
    83         # entity's attributes and relations, excluding meta data
    50         # if the entity isn't meta itself
    84         # if the entity isn't meta itself
    51         boxes = self._preinit_side_related(entity)
    85         boxes = self._prepare_side_boxes(entity)
    52         if boxes:
    86         if boxes or hasattr(self, 'render_side_related'):
    53             self.w(u'<table width="100%"><tr><td width="75%">')
    87             self.w(u'<table width="100%"><tr><td width="75%">')
    54         self.w(u'<div>')
    88         self.w(u'<div>')
    55         self.w(u'<div class="mainInfo">')
    89         self.w(u'<div class="mainInfo">')
    56         try:
    90         try:
    57             self.render_entity_attributes(entity)
    91             self.render_entity_attributes(entity)
    67             except TypeError: # XXX bw compat
   101             except TypeError: # XXX bw compat
    68                 warn('siderelations argument of render_entity_relations is '
   102                 warn('siderelations argument of render_entity_relations is '
    69                      'deprecated')
   103                      'deprecated')
    70                 self.render_entity_relations(entity, [])
   104                 self.render_entity_relations(entity, [])
    71         self.w(u'</div>')
   105         self.w(u'</div>')
    72         if boxes:
   106         if boxes or hasattr(self, 'render_side_related'):
    73             self.w(u'</td><td>')
   107             self.w(u'</td><td>')
    74             # side boxes
   108             # side boxes
    75             self.w(u'<div class="primaryRight">')
   109             self.w(u'<div class="primaryRight">')
    76             try:
   110             if hasattr(self, 'render_side_related'):
    77                 self.render_side_related(entity)
   111                 warn('render_side_related is deprecated')
    78             except TypeError: # XXX bw compat
   112                 self.render_side_related(entity, [])
    79                 warn('siderelations argument of render_entity_relations is '
   113             self.render_side_boxes(boxes)
    80                      'deprecated')
       
    81                 self.render_entity_relations(entity, [])
       
    82             self.w(u'</div>')
   114             self.w(u'</div>')
    83             self.w(u'</td></tr></table>')
   115             self.w(u'</td></tr></table>')
    84         self.content_navigation_components('navcontentbottom')
   116         self.content_navigation_components('navcontentbottom')
    85 
   117 
    86 
   118 
    95                 warn('component %s doesnt implement cell_call, please update'
   127                 warn('component %s doesnt implement cell_call, please update'
    96                      % comp.__class__, DeprecationWarning)
   128                      % comp.__class__, DeprecationWarning)
    97                 comp.dispatch(w=self.w, view=self)
   129                 comp.dispatch(w=self.w, view=self)
    98         self.w(u'</div>')
   130         self.w(u'</div>')
    99 
   131 
   100     def iter_attributes(self, entity):
       
   101         for rschema, targetschema in entity.e_schema.attribute_definitions():
       
   102             if rschema.type in self.skip_attrs:
       
   103                 continue
       
   104             yield rschema, targetschema
       
   105 
       
   106     def iter_relations(self, entity):
       
   107         skip = set(self.skip_rels)
       
   108         skip.update(PRIMARY_SKIP_RELS)
       
   109         for rschema, targetschemas, x in entity.e_schema.relation_definitions():
       
   110             if rschema.type in skip:
       
   111                 continue
       
   112             yield rschema, targetschemas, x
       
   113 
       
   114     def render_entity_title(self, entity):
   132     def render_entity_title(self, entity):
   115         title = self.content_title(entity) # deprecate content_title?
   133         title = self.content_title(entity) # deprecate content_title?
   116         if title:
   134         if title:
   117             self.w(u'<h1><span class="etype">%s</span> %s</h1>'
   135             self.w(u'<h1><span class="etype">%s</span> %s</h1>'
   118                    % (entity.dc_type().capitalize(), title))
   136                    % (entity.dc_type().capitalize(), title))
   119 
   137 
       
   138 
   120     def content_title(self, entity):
   139     def content_title(self, entity):
   121         """default implementation return an empty string"""
   140         """default implementation return dc_title"""
   122         return u''
   141         return html_escape(entity.dc_title())
   123 
   142 
   124     def render_entity_metadata(self, entity):
   143     def render_entity_metadata(self, entity):
   125         entity.view('metadata', w=self.w)
   144         entity.view('metadata', w=self.w)
   126         summary = self.summary(entity) # deprecate summary?
   145         summary = self.summary(entity) # deprecate summary?
   127         if summary:
   146         if summary:
   130     def summary(self, entity):
   149     def summary(self, entity):
   131         """default implementation return an empty string"""
   150         """default implementation return an empty string"""
   132         return u''
   151         return u''
   133 
   152 
   134     def render_entity_attributes(self, entity, siderelations=None):
   153     def render_entity_attributes(self, entity, siderelations=None):
   135         for rschema, targetschema in self.iter_attributes(entity):
   154         for rschema, tschemas, role, displayinfo in self._iter_display(entity, 'attributes'):
   136             attr = rschema.type
   155             vid =  displayinfo.get('vid', 'reledit')
   137             if targetschema.type in ('Password', 'Bytes'):
   156             if rschema.is_final():
   138                 continue
   157                 value = entity.view('reledit', rtype=rschema.type)
   139             try:
   158             else:
   140                 wdg = entity.get_widget(attr)
   159                 vid =  displayinfo.get('vid', 'reledit')
   141             except Exception, ex:
   160                 rset = self._relation_rset(entity, rschema, role, displayinfo)
   142                 value = entity.printable_value(attr, entity[attr], targetschema.type)
   161                 if rset:
   143             else:
   162                     value = self.view(rset, vid)
   144                 value = wdg.render(entity)
   163                 else:
       
   164                     value = None
   145             if self.skip_none and (value is None or value == ''):
   165             if self.skip_none and (value is None or value == ''):
   146                 continue
   166                 continue
   147             if rschema.meta:
   167             self._render_attribute(rschema, value)
   148                 continue
       
   149             self._render_related_entities(entity, rschema, value)
       
   150 
       
   151     def _preinit_side_related(self, entity):
       
   152         self._sideboxes = []
       
   153         if hasattr(self, 'get_side_boxes_defs'):
       
   154             self._sideboxes = [(label, rset, 'sidebox') for label, rset in self.get_side_boxes_defs(entity)
       
   155                                if rset]
       
   156         else:
       
   157             eschema = entity.e_schema
       
   158             maxrelated = self.req.property_value('navigation.related-limit')
       
   159             for rschema, targetschemas, role in self.iter_relations(entity):
       
   160                 if self.is_side_related(rschema, eschema):
       
   161                     try:
       
   162                         related = entity.related(rschema.type, role, limit=maxrelated+1)
       
   163                     except Unauthorized:
       
   164                         continue
       
   165                     if not related:
       
   166                         continue
       
   167                     label = display_name(self.req, rschema.type, role)
       
   168                     self._sideboxes.append((label, related, 'autolimited'))
       
   169         self._contextboxes = list(self.vreg.possible_vobjects('boxes', self.req, self.rset,
       
   170                                                                   row=self.row, view=self,
       
   171                                                                   context='incontext'))
       
   172         return self._sideboxes or self._contextboxes
       
   173 
   168 
   174     def render_entity_relations(self, entity, siderelations=None):
   169     def render_entity_relations(self, entity, siderelations=None):
   175         eschema = entity.e_schema
   170         for rschema, tschemas, role, displayinfo in self._iter_display(entity, 'relations'):
   176         for rschema, targetschemas, x in self.iter_relations(entity):
   171             rset = self._relation_rset(entity, rschema, role, displayinfo)
   177             if not self.is_side_related(rschema, eschema):
   172             if rset:
   178                 try:
   173                 self._render_relation(rset, displayinfo, 'autolimited',
   179                     related = entity.related(rschema.type, x, limit=maxrelated+1)
   174                                       self.show_rel_label)
   180                 except Unauthorized:
   175 
   181                     continue
   176     def render_side_boxes(self, boxes):
   182                 self._render_related_entities(entity, rschema, related, x)
       
   183 
       
   184 
       
   185     def render_side_related(self, entity, siderelations=None):
       
   186         """display side related relations:
   177         """display side related relations:
   187         non-meta in a first step, meta in a second step
   178         non-meta in a first step, meta in a second step
   188         """
   179         """
   189         if self._sideboxes:
   180         for box in boxes:
   190             for label, rset, vid in self._sideboxes:
   181             if isinstance(box, tuple):
       
   182                 label, rset, vid, _  = box
   191                 self.w(u'<div class="sideRelated">')
   183                 self.w(u'<div class="sideRelated">')
   192                 self.wview(vid, rset, title=label)
   184                 self.wview(vid, rset, title=label)
   193                 self.w(u'</div>')
   185                 self.w(u'</div>')
   194         if self._contextboxes:
   186             else:
   195             for box in self._contextboxes:
       
   196                 try:
   187                 try:
   197                     box.dispatch(w=self.w, row=self.row)
   188                     box.dispatch(w=self.w, row=self.row)
   198                 except NotImplementedError:
   189                 except NotImplementedError:
   199                     # much probably a context insensitive box, which only implements
   190                     # much probably a context insensitive box, which only implements
   200                     # .call() and not cell_call()
   191                     # .call() and not cell_call()
   201                     box.dispatch(w=self.w)
   192                     box.dispatch(w=self.w)
   202 
   193 
   203     def is_side_related(self, rschema, eschema):
   194     def _prepare_side_boxes(self, entity):
   204         return rschema.meta and \
   195         sideboxes = []
   205                not rschema.schema_relation() == eschema.schema_entity()
   196         for rschema, tschemas, role, displayinfo in self._iter_display(entity, 'sideboxes'):
   206 
   197             rset = self._relation_rset(entity, rschema, role, displayinfo)
   207     def _render_related_entities(self, entity, rschema, related,
   198             if not rset:
   208                                  role='subject'):
   199                 continue
       
   200             label = display_name(self.req, rschema.type, role)
       
   201             vid = displayinfo.get('vid', 'autolimited')
       
   202             sideboxes.append((label, rset, vid, displayinfo.get('order')))
       
   203         sideboxes = sorted(sideboxes, key=lambda x: x[-1])
       
   204         sideboxes += list(self.vreg.possible_vobjects('boxes', self.req, self.rset,
       
   205                                                       row=self.row, view=self,
       
   206                                                       context='incontext'))
       
   207         return sideboxes
       
   208 
       
   209     def _iter_display(self, entity, where):
       
   210         eschema = entity.e_schema
       
   211         for rschema, tschemas, role in eschema.relation_definitions(True):
       
   212             matchtschemas = []
       
   213             for tschema in tschemas:
       
   214                 displayinfo = self.rdisplay.etype_get(eschema, rschema, role,
       
   215                                                       tschema)
       
   216                 assert displayinfo is not None, (str(rschema), role,
       
   217                                                  str(eschema), str(tschema))
       
   218                 if displayinfo.get('where') == where:
       
   219                     matchtschemas.append(tschema)
       
   220             if matchtschemas:
       
   221                 # XXX pick the latest displayinfo
       
   222                 yield rschema, matchtschemas, role, displayinfo
       
   223 
       
   224     def _relation_rset(self, entity, rschema, role, displayinfo):
       
   225         try:
       
   226             if displayinfo.get('limit'):
       
   227                 rset = entity.related(rschema.type, role,
       
   228                                       limit=self.maxrelated+1)
       
   229             else:
       
   230                 rset = entity.related(rschema.type, role)
       
   231         except Unauthorized:
       
   232             return
       
   233         if 'filter' in displayinfo:
       
   234             rset = displayinfo['filter'](rset)
       
   235         return rset
       
   236 
       
   237     def _render_relation(self, rset, displayinfo, defaultvid, showlabel):
       
   238         self.w('<div class="section">')
       
   239         if showlabel:
       
   240             label = self.req._(displayinfo['label'])
       
   241             self.w('<h4>%s</h4>' % label)
       
   242         self.wview(displayinfo.get('vid', defaultvid), rset)
       
   243         self.w('</div>')
       
   244 
       
   245     def _render_attribute(self, rschema, value, role='subject'):
   209         if rschema.is_final():
   246         if rschema.is_final():
   210             value = related
       
   211             show_label = self.show_attr_label
   247             show_label = self.show_attr_label
   212         else:
   248         else:
   213             if not related:
   249             show_label = self.show_rel_label
   214                 return
       
   215             value = self.view('autolimited', related)
       
   216         label = display_name(self.req, rschema.type, role)
   250         label = display_name(self.req, rschema.type, role)
   217         self.field(label, value, show_label=show_label, tr=False)
   251         self.field(label, value, show_label=show_label, tr=False)
   218 
   252 
   219 
   253 
   220 class RelatedView(EntityView):
   254 class RelatedView(EntityView):
   221     id = 'autolimited'
   255     id = 'autolimited'
   222     def call(self):
   256     def call(self, title=None, **kwargs):
   223         # if not too many entities, show them all in a list
   257         # if not too many entities, show them all in a list
   224         maxrelated = self.req.property_value('navigation.related-limit')
   258         maxrelated = self.req.property_value('navigation.related-limit')
       
   259         if title:
       
   260             self.w(u'<div class="title"><span>%s</span></div>' % title)
   225         if self.rset.rowcount <= maxrelated:
   261         if self.rset.rowcount <= maxrelated:
   226             if self.rset.rowcount == 1:
   262             if self.rset.rowcount == 1:
   227                 self.wview('incontext', self.rset, row=0)
   263                 self.wview('incontext', self.rset, row=0)
   228             elif 1 < self.rset.rowcount <= 5:
   264             elif 1 < self.rset.rowcount <= 5:
   229                 self.wview('csv', self.rset)
   265                 self.wview('csv', self.rset)
   232                 self.wview('simplelist', self.rset)
   268                 self.wview('simplelist', self.rset)
   233                 self.w(u'</div>')
   269                 self.w(u'</div>')
   234         # else show links to display related entities
   270         # else show links to display related entities
   235         else:
   271         else:
   236             rql = self.rset.printable_rql()
   272             rql = self.rset.printable_rql()
   237             self.rset.limit(maxself.rset)
   273             self.rset.limit(maxrelated)
   238             self.w(u'<div>')
   274             self.w(u'<div>')
   239             self.wview('simplelist', self.rset)
   275             self.wview('simplelist', self.rset)
   240             self.w(u'[<a href="%s">%s</a>]' % (self.build_url(rql=rql),
   276             self.w(u'[<a href="%s">%s</a>]' % (self.build_url(rql=rql),
   241                                                self.req._('see them all')))
   277                                                self.req._('see them all')))
   242             self.w(u'</div>')
   278             self.w(u'</div>')