web/views/primary.py
branchtls-sprint
changeset 1554 3a3263df6cdd
parent 1516 288d55a7c5e2
child 1558 f63d090eb34a
--- a/web/views/primary.py	Thu Apr 30 00:41:10 2009 +0200
+++ b/web/views/primary.py	Thu Apr 30 00:41:52 2009 +0200
@@ -4,19 +4,18 @@
 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
+__docformat__ = "restructuredtext en"
 
 from warnings import warn
 
+from logilab.mtconverter import html_escape
+
 from cubicweb import Unauthorized
 from cubicweb.view import EntityView
+from cubicweb.web.uicfg import rdisplay
 
 _ = unicode
 
-PRIMARY_SKIP_RELS = set(['is', 'is_instance_of', 'identity',
-                         'owned_by', 'created_by',
-                         'in_state', 'wf_info_for', 'require_permission',
-                         'from_entity', 'to_entity',
-                         'see_also'])
 
 class PrimaryView(EntityView):
     """the full view of an non final entity"""
@@ -25,10 +24,44 @@
     show_attr_label = True
     show_rel_label = True
     skip_none = True
-    skip_attrs = ('eid', 'creation_date', 'modification_date')
-    skip_rels = ()
+    rdisplay = rdisplay
     main_related_section = True
 
+    @classmethod
+    def vreg_initialization_completed(cls):
+        """set default category tags for relations where it's not yet defined in
+        the category relation tags
+        """
+        for eschema in cls.schema.entities():
+            for rschema, tschemas, role in eschema.relation_definitions(True):
+                for tschema in tschemas:
+                    if role == 'subject':
+                        X, Y = eschema, tschema
+                        card = rschema.rproperty(X, Y, 'cardinality')[0]
+                        composed = rschema.rproperty(X, Y, 'composite') == 'object'
+                    else:
+                        X, Y = tschema, eschema
+                        card = rschema.rproperty(X, Y, 'cardinality')[1]
+                        composed = rschema.rproperty(X, Y, 'composite') == 'subject'
+                    displayinfo = cls.rdisplay.get(rschema, role, X, Y)
+                    if displayinfo is None:
+                        if rschema.is_final():
+                            if rschema.meta or tschema.type in ('Password', 'Bytes'):
+                                where = None
+                            else:
+                                where = 'attributes'
+                        elif card in '1+':
+                            where = 'attributes'
+                        elif composed:
+                            where = 'relations'
+                        else:
+                            where = 'sideboxes'
+                        displayinfo = {'where': where,
+                                       'order': cls.rdisplay.get_timestamp()}
+                        cls.rdisplay.tag_relation(displayinfo, (X, rschema, Y),
+                                                  role)
+                    displayinfo.setdefault('label', '%s_%s' % (rschema, role))
+
     def html_headers(self):
         """return a list of html headers (eg something to be inserted between
         <head> and </head> of the returned page
@@ -41,6 +74,7 @@
         self.row = row
         # XXX move render_entity implementation here
         self.render_entity(self.complete_entity(row, col))
+        self.maxrelated = self.req.property_value('navigation.related-limit')
 
     def render_entity(self, entity):
         """return html to display the given entity"""
@@ -48,8 +82,8 @@
         self.render_entity_metadata(entity)
         # entity's attributes and relations, excluding meta data
         # if the entity isn't meta itself
-        boxes = self._preinit_side_related(entity)
-        if boxes:
+        boxes = self._prepare_side_boxes(entity)
+        if boxes or hasattr(self, 'render_side_related'):
             self.w(u'<table width="100%"><tr><td width="75%">')
         self.w(u'<div>')
         self.w(u'<div class="mainInfo">')
@@ -69,16 +103,14 @@
                      'deprecated')
                 self.render_entity_relations(entity, [])
         self.w(u'</div>')
-        if boxes:
+        if boxes or hasattr(self, 'render_side_related'):
             self.w(u'</td><td>')
             # side boxes
             self.w(u'<div class="primaryRight">')
-            try:
-                self.render_side_related(entity)
-            except TypeError: # XXX bw compat
-                warn('siderelations argument of render_entity_relations is '
-                     'deprecated')
-                self.render_entity_relations(entity, [])
+            if hasattr(self, 'render_side_related'):
+                warn('render_side_related is deprecated')
+                self.render_side_related(entity, [])
+            self.render_side_boxes(boxes)
             self.w(u'</div>')
             self.w(u'</td></tr></table>')
         self.content_navigation_components('navcontentbottom')
@@ -97,29 +129,16 @@
                 comp.dispatch(w=self.w, view=self)
         self.w(u'</div>')
 
-    def iter_attributes(self, entity):
-        for rschema, targetschema in entity.e_schema.attribute_definitions():
-            if rschema.type in self.skip_attrs:
-                continue
-            yield rschema, targetschema
-
-    def iter_relations(self, entity):
-        skip = set(self.skip_rels)
-        skip.update(PRIMARY_SKIP_RELS)
-        for rschema, targetschemas, x in entity.e_schema.relation_definitions():
-            if rschema.type in skip:
-                continue
-            yield rschema, targetschemas, x
-
     def render_entity_title(self, entity):
         title = self.content_title(entity) # deprecate content_title?
         if title:
             self.w(u'<h1><span class="etype">%s</span> %s</h1>'
                    % (entity.dc_type().capitalize(), title))
 
+
     def content_title(self, entity):
-        """default implementation return an empty string"""
-        return u''
+        """default implementation return dc_title"""
+        return html_escape(entity.dc_title())
 
     def render_entity_metadata(self, entity):
         entity.view('metadata', w=self.w)
@@ -132,67 +151,39 @@
         return u''
 
     def render_entity_attributes(self, entity, siderelations=None):
-        for rschema, targetschema in self.iter_attributes(entity):
-            attr = rschema.type
-            if targetschema.type in ('Password', 'Bytes'):
-                continue
-            try:
-                wdg = entity.get_widget(attr)
-            except Exception, ex:
-                value = entity.printable_value(attr, entity[attr], targetschema.type)
+        for rschema, tschemas, role, displayinfo in self._iter_display(entity, 'attributes'):
+            vid =  displayinfo.get('vid', 'reledit')
+            if rschema.is_final():
+                value = entity.view('reledit', rtype=rschema.type)
             else:
-                value = wdg.render(entity)
+                vid =  displayinfo.get('vid', 'reledit')
+                rset = self._relation_rset(entity, rschema, role, displayinfo)
+                if rset:
+                    value = self.view(rset, vid)
+                else:
+                    value = None
             if self.skip_none and (value is None or value == ''):
                 continue
-            if rschema.meta:
-                continue
-            self._render_related_entities(entity, rschema, value)
-
-    def _preinit_side_related(self, entity):
-        self._sideboxes = []
-        if hasattr(self, 'get_side_boxes_defs'):
-            self._sideboxes = [(label, rset, 'sidebox') for label, rset in self.get_side_boxes_defs(entity)
-                               if rset]
-        else:
-            eschema = entity.e_schema
-            maxrelated = self.req.property_value('navigation.related-limit')
-            for rschema, targetschemas, role in self.iter_relations(entity):
-                if self.is_side_related(rschema, eschema):
-                    try:
-                        related = entity.related(rschema.type, role, limit=maxrelated+1)
-                    except Unauthorized:
-                        continue
-                    if not related:
-                        continue
-                    label = display_name(self.req, rschema.type, role)
-                    self._sideboxes.append((label, related, 'autolimited'))
-        self._contextboxes = list(self.vreg.possible_vobjects('boxes', self.req, self.rset,
-                                                                  row=self.row, view=self,
-                                                                  context='incontext'))
-        return self._sideboxes or self._contextboxes
+            self._render_attribute(rschema, value)
 
     def render_entity_relations(self, entity, siderelations=None):
-        eschema = entity.e_schema
-        for rschema, targetschemas, x in self.iter_relations(entity):
-            if not self.is_side_related(rschema, eschema):
-                try:
-                    related = entity.related(rschema.type, x, limit=maxrelated+1)
-                except Unauthorized:
-                    continue
-                self._render_related_entities(entity, rschema, related, x)
+        for rschema, tschemas, role, displayinfo in self._iter_display(entity, 'relations'):
+            rset = self._relation_rset(entity, rschema, role, displayinfo)
+            if rset:
+                self._render_relation(rset, displayinfo, 'autolimited',
+                                      self.show_rel_label)
 
-
-    def render_side_related(self, entity, siderelations=None):
+    def render_side_boxes(self, boxes):
         """display side related relations:
         non-meta in a first step, meta in a second step
         """
-        if self._sideboxes:
-            for label, rset, vid in self._sideboxes:
+        for box in boxes:
+            if isinstance(box, tuple):
+                label, rset, vid, _  = box
                 self.w(u'<div class="sideRelated">')
                 self.wview(vid, rset, title=label)
                 self.w(u'</div>')
-        if self._contextboxes:
-            for box in self._contextboxes:
+            else:
                 try:
                     box.dispatch(w=self.w, row=self.row)
                 except NotImplementedError:
@@ -200,28 +191,73 @@
                     # .call() and not cell_call()
                     box.dispatch(w=self.w)
 
-    def is_side_related(self, rschema, eschema):
-        return rschema.meta and \
-               not rschema.schema_relation() == eschema.schema_entity()
+    def _prepare_side_boxes(self, entity):
+        sideboxes = []
+        for rschema, tschemas, role, displayinfo in self._iter_display(entity, 'sideboxes'):
+            rset = self._relation_rset(entity, rschema, role, displayinfo)
+            if not rset:
+                continue
+            label = display_name(self.req, rschema.type, role)
+            vid = displayinfo.get('vid', 'autolimited')
+            sideboxes.append((label, rset, vid, displayinfo.get('order')))
+        sideboxes = sorted(sideboxes, key=lambda x: x[-1])
+        sideboxes += list(self.vreg.possible_vobjects('boxes', self.req, self.rset,
+                                                      row=self.row, view=self,
+                                                      context='incontext'))
+        return sideboxes
 
-    def _render_related_entities(self, entity, rschema, related,
-                                 role='subject'):
+    def _iter_display(self, entity, where):
+        eschema = entity.e_schema
+        for rschema, tschemas, role in eschema.relation_definitions(True):
+            matchtschemas = []
+            for tschema in tschemas:
+                displayinfo = self.rdisplay.etype_get(eschema, rschema, role,
+                                                      tschema)
+                assert displayinfo is not None, (str(rschema), role,
+                                                 str(eschema), str(tschema))
+                if displayinfo.get('where') == where:
+                    matchtschemas.append(tschema)
+            if matchtschemas:
+                # XXX pick the latest displayinfo
+                yield rschema, matchtschemas, role, displayinfo
+
+    def _relation_rset(self, entity, rschema, role, displayinfo):
+        try:
+            if displayinfo.get('limit'):
+                rset = entity.related(rschema.type, role,
+                                      limit=self.maxrelated+1)
+            else:
+                rset = entity.related(rschema.type, role)
+        except Unauthorized:
+            return
+        if 'filter' in displayinfo:
+            rset = displayinfo['filter'](rset)
+        return rset
+
+    def _render_relation(self, rset, displayinfo, defaultvid, showlabel):
+        self.w('<div class="section">')
+        if showlabel:
+            label = self.req._(displayinfo['label'])
+            self.w('<h4>%s</h4>' % label)
+        self.wview(displayinfo.get('vid', defaultvid), rset)
+        self.w('</div>')
+
+    def _render_attribute(self, rschema, value, role='subject'):
         if rschema.is_final():
-            value = related
             show_label = self.show_attr_label
         else:
-            if not related:
-                return
-            value = self.view('autolimited', related)
+            show_label = self.show_rel_label
         label = display_name(self.req, rschema.type, role)
         self.field(label, value, show_label=show_label, tr=False)
 
 
 class RelatedView(EntityView):
     id = 'autolimited'
-    def call(self):
+    def call(self, title=None, **kwargs):
         # if not too many entities, show them all in a list
         maxrelated = self.req.property_value('navigation.related-limit')
+        if title:
+            self.w(u'<div class="title"><span>%s</span></div>' % title)
         if self.rset.rowcount <= maxrelated:
             if self.rset.rowcount == 1:
                 self.wview('incontext', self.rset, row=0)
@@ -234,7 +270,7 @@
         # else show links to display related entities
         else:
             rql = self.rset.printable_rql()
-            self.rset.limit(maxself.rset)
+            self.rset.limit(maxrelated)
             self.w(u'<div>')
             self.wview('simplelist', self.rset)
             self.w(u'[<a href="%s">%s</a>]' % (self.build_url(rql=rql),