merge tls-sprint
authorsylvain.thenault@logilab.fr
Thu, 30 Apr 2009 08:51:54 +0200
branchtls-sprint
changeset 1564 de0548cb0ab8
parent 1558 f63d090eb34a (diff)
parent 1563 b130c6cec8c2 (current diff)
child 1565 cc68fc4ae11e
merge
web/form.py
web/formfields.py
web/formwidgets.py
--- a/entities/authobjs.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/entities/authobjs.py	Thu Apr 30 08:51:54 2009 +0200
@@ -24,7 +24,7 @@
     id = 'CWUser'
     fetch_attrs, fetch_order = fetch_config(['login', 'firstname', 'surname'])
     fetch_unrelated_order = fetch_order
-    
+
     # used by repository to check if  the user can log in or not
     AUTHENTICABLE_STATES = ('activated',)
 
@@ -37,7 +37,7 @@
             self._groups = groups
         if properties is not None:
             self._properties = properties
-            
+
     @property
     def groups(self):
         try:
@@ -45,7 +45,7 @@
         except AttributeError:
             self._groups = set(g.name for g in self.in_group)
             return self._groups
-        
+
     @property
     def properties(self):
         try:
@@ -64,7 +64,7 @@
         except ValueError:
             self.warning('incorrect value for eproperty %s of user %s', key, self.login)
         return self.vreg.property_value(key)
-    
+
     def matching_groups(self, groups):
         """return the number of the given group(s) in which the user is
 
@@ -86,7 +86,7 @@
         """ checks if user is an anonymous user"""
         #FIXME on the web-side anonymous user is detected according
         # to config['anonymous-user'], we don't have this info on
-        # the server side. 
+        # the server side.
         return self.groups == frozenset(('guests', ))
 
     def owns(self, eid):
@@ -116,12 +116,12 @@
             return self.req.execute(rql, kwargs, cachekey)
         except Unauthorized:
             return False
-    
+
     # presentation utilities ##################################################
-    
+
     def name(self):
         """construct a name using firstname / surname or login if not defined"""
-        
+
         if self.firstname and self.surname:
             return self.req._('%(firstname)s %(surname)s') % {
                 'firstname': self.firstname, 'surname' : self.surname}
--- a/etwist/server.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/etwist/server.py	Thu Apr 30 08:51:54 2009 +0200
@@ -44,6 +44,14 @@
     # ensure no tasks will be further added
     repo._looping_tasks = ()
 
+def host_prefixed_baseurl(baseurl, host):
+    scheme, netloc, url, query, fragment = urlsplit(baseurl)
+    netloc_domain = '.' + '.'.join(netloc.split('.')[-2:])
+    if host.endswith(netloc_domain):
+        netloc = host
+    baseurl = urlunsplit((scheme, netloc, url, query, fragment))
+    return baseurl
+
 
 class LongTimeExpiringFile(static.File):
     """overrides static.File and sets a far futre ``Expires`` date
@@ -169,11 +177,8 @@
             https = False
             baseurl = self.base_url
         if self.config['use-request-subdomain']:
-            scheme, netloc, url, query, fragment = urlsplit(baseurl)
-            if '.' in netloc:
-                netloc = '.'.join(host.split('.')[:1] + netloc.split('.')[1:])
-            baseurl = urlunsplit((scheme, netloc, url, query, fragment))
-            self.warning('base_url is %s for this request', baseurl)
+            baseurl = host_prefixed_baseurl(baseurl, host)
+            self.warning('used baseurl is %s for this request', baseurl)
         req = CubicWebTwistedRequestAdapter(request, self.appli.vreg, https, baseurl)
         if req.authmode == 'http':
             # activate realm-based auth
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/etwist/test/unittest_server.py	Thu Apr 30 08:51:54 2009 +0200
@@ -0,0 +1,35 @@
+from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.etwist.server import host_prefixed_baseurl
+
+
+class HostPrefixedBaseURLTC(EnvBasedTC):
+
+    def _check(self, baseurl, host, waited):
+        self.assertEquals(host_prefixed_baseurl(baseurl, host), waited,
+                          'baseurl %s called through host %s should be considered as %s'
+                          % (baseurl, host, waited))
+
+    def test1(self):
+        self._check('http://www.cubicweb.org/hg/', 'code.cubicweb.org',
+                    'http://code.cubicweb.org/hg/')
+
+    def test2(self):
+        self._check('http://www.cubicweb.org/hg/', 'cubicweb.org',
+                    'http://www.cubicweb.org/hg/')
+
+    def test3(self):
+        self._check('http://cubicweb.org/hg/', 'code.cubicweb.org',
+                    'http://code.cubicweb.org/hg/')
+
+    def test4(self):
+        self._check('http://www.cubicweb.org/hg/', 'localhost',
+                    'http://www.cubicweb.org/hg/')
+
+    def test5(self):
+        self._check('http://www.cubicweb.org/cubes/', 'hg.code.cubicweb.org',
+                    'http://hg.code.cubicweb.org/cubes/')
+
+    def test6(self):
+        self._check('http://localhost:8080/hg/', 'code.cubicweb.org',
+                    'http://localhost:8080/hg/')
+
--- a/goa/skel/views.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/goa/skel/views.py	Thu Apr 30 08:51:54 2009 +0200
@@ -1,10 +1,8 @@
 # custom application views
-
-from mx.DateTime import DateTime
+from calendar import monthrange
+from datetime import date
 
-from cubicweb.web.views import baseviews
-from cubicweb.web.views.boxes import BoxTemplate
-from cubicweb.web.views.calendar import MONTHNAMES
+from cubicweb.web.views import baseviews, boxes, calendar
 from cubicweb.web.htmlwidgets import BoxLink, BoxWidget
 
 _ = unicode
@@ -12,15 +10,15 @@
 
 class BlogEntryPrimaryView(baseviews.PrimaryView):
     accepts = ('BlogEntry',)
-    
+
     def cell_call(self, row, col):
         entity = self.entity(row, col)
         self.w(u'<h1>%s</h1>' % entity.dc_title())
         entity.view('metadata', w=self.w)
         self.w(entity.printable_value('text'))
-        
+
 
-class BlogArchiveBox(BoxTemplate):
+class BlogArchiveBox(boxes.BoxTemplate):
     """side box usually displaying some related entities in a primary view"""
     id = 'blog_archives_box'
     title = _('blog archives')
@@ -37,12 +35,12 @@
                 blogmonths.append( (year, month) )
         box = BoxWidget(_('Blog archives'), id=self.id)
         for year, month in blogmonths:
-            firstday = DateTime(year, month, 1)
-            lastday = DateTime(year, month, firstday.days_in_month)
+            firstday = date(year, month, 1)
+            lastday = date(year, month, monthrange(year, month)[1])
             rql = ('Any B WHERE B is BlogEntry, B creation_date >= "%s", B creation_date <= "%s"'
                    % (firstday.strftime('%Y-%m-%d'), lastday.strftime('%Y-%m-%d')))
             url = self.build_url(rql=rql)
-            label = u'%s %s' % (_(MONTHNAMES[month-1]), year)
+            label = u'%s %s' % (_(calendar.MONTHNAMES[month-1]), year)
             box.append( BoxLink(url, label) )
         box.render(self.w)
 
--- a/interfaces.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/interfaces.py	Thu Apr 30 08:51:54 2009 +0200
@@ -11,7 +11,7 @@
 
 class IEmailable(Interface):
     """interface for emailable entities"""
-    
+
     def get_email(self):
         """return email address"""
 
@@ -28,7 +28,7 @@
     def as_email_context(self):
         """returns the dictionary as used by the sendmail controller to
         build email bodies.
-        
+
         NOTE: the dictionary keys should match the list returned by the
         `allowed_massmail_keys` method.
         """
@@ -45,12 +45,12 @@
         """change the entity's state according to a state defined in given
         parameters
         """
-    
+
     def can_pass_transition(self, trname):
         """return true if the current user can pass the transition with the
         given name
         """
-    
+
     def latest_trinfo(self):
         """return the latest transition information for this entity
         """
@@ -73,7 +73,7 @@
     @property
     def todo(self):
         """what remains to be done"""
-    
+
     def progress_info(self):
         """returns a dictionary describing progress/estimated cost of the
         version.
@@ -93,19 +93,19 @@
 
     def progress(self):
         """returns the % progress of the task item"""
-        
-    
+
+
 class IMileStone(IProgress):
     """represents an ITask's item"""
-    
+
     parent_type = None # specify main task's type
-    
+
     def get_main_task(self):
         """returns the main ITask entity"""
 
     def initial_prevision_date(self):
         """returns the initial expected end of the milestone"""
-        
+
     def eta_date(self):
         """returns expected date of completion based on what remains
         to be done
@@ -128,7 +128,7 @@
 
     def __iter__(self):
         """iterates over the item's children"""
-        
+
     def is_leaf(self):
         """returns true if this node as no child"""
 
@@ -146,7 +146,7 @@
     """interface for entities which can be linked to a previous and/or next
     entity
     """
-    
+
     def next_entity(self):
         """return the 'next' entity"""
     def previous_entity(self):
@@ -155,10 +155,10 @@
 
 class IBreadCrumbs(Interface):
     """interface for entities which can be "located" on some path"""
-    
+
     def breadcrumbs(self, view, recurs=False):
         """return a list containing some:
-        
+
         * tuple (url, label)
         * entity
         * simple label string
@@ -173,7 +173,7 @@
 
 class IDownloadable(Interface):
     """interface for downloadable entities"""
-    
+
     def download_url(self): # XXX not really part of this interface
         """return an url to download entity's content"""
     def download_content_type(self):
@@ -188,31 +188,31 @@
 
 class IEmbedable(Interface):
     """interface for embedable entities"""
-    
+
     def embeded_url(self):
         """embed action interface"""
-    
+
 class ICalendarable(Interface):
     """interface for items that do have a begin date 'start' and an end date 'stop'
-    """    
-    
+    """
+
 class ICalendarViews(Interface):
     """calendar views interface"""
     def matching_dates(self, begin, end):
         """
         :param begin: day considered as begin of the range (`DateTime`)
         :param end: day considered as end of the range (`DateTime`)
-        
+
         :return:
           a list of dates (`DateTime`) in the range [`begin`, `end`] on which
           this entity apply
         """
-        
+
 class ITimetableViews(Interface):
     """timetable views interface"""
     def timetable_date(self):
         """XXX explain
-        
+
         :return: date (`DateTime`)
         """
 
@@ -231,17 +231,18 @@
         """returns the icon that should be used as the marker
         (returns None for default)
         """
-        
+
 class IFeed(Interface):
     """interface for entities with rss flux"""
-    
+
     def rss_feed_url(self):
         """return an url which layout sub-entities item
         """
+
 class ISiocItem(Interface):
     """interface for entities (which are item
     in sioc specification) with sioc views"""
-    
+
     def isioc_content(self):
         """return content entity"""
 
@@ -252,11 +253,11 @@
         """return container type (post, BlogPost, MailMessage)"""
 
     def isioc_replies(self):
-        """return replies items"""       
+        """return replies items"""
 
     def isioc_topics(self):
         """return topics items"""
-            
+
 class ISiocContainer(Interface):
     """interface for entities (which are container
     in sioc specification) with sioc views"""
@@ -267,5 +268,5 @@
     def isioc_items(self):
         """return contained items"""
 
-   
-    
+
+
--- a/rtags.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/rtags.py	Thu Apr 30 08:51:54 2009 +0200
@@ -42,6 +42,9 @@
                 keys.remove((rtype, tagged, stype, '*'))
         return keys
 
+    def tag_attribute(self, tag, stype, attr):
+        self._tagdefs[(str(attr), 'subject', str(stype), '*')] = tag
+
     def tag_relation(self, tag, relation, tagged):
         assert tagged in ('subject', 'object'), tagged
         stype, rtype, otype = relation
--- a/utils.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/utils.py	Thu Apr 30 08:51:54 2009 +0200
@@ -8,10 +8,11 @@
 
 import locale
 from md5 import md5
+from calendar import monthrange
 from datetime import datetime, timedelta, date
 from time import time
 from random import randint, seed
-    
+
 # initialize random seed from current time
 seed()
 
@@ -29,26 +30,43 @@
     assert isinstance(somedate, date)
     return date
 
+ONEDAY = timedelta(days=1)
+
+def days_in_month(date_):
+    return monthrange(date_.year, date_.month)[1]
+
+def previous_month(date_):
+    return first_day(date_) - ONEDAY
+
+def next_month(date_):
+    return last_day(date_) + ONEDAY
+
+def first_day(date_):
+    return date(date_.year, date_.month, 1)
+
+def last_day(date_):
+    return date(date_.year, date_.month, days_in_month(date_))
+
 def date_range(begin, end, incr=1, include=None):
     """yields each date between begin and end
     :param begin: the start date
     :param end: the end date
     :param incr: the step to use to iterate over dates. Default is
-                 one day.                 
+                 one day.
     :param include: None (means no exclusion) or a function taking a
                     date as parameter, and returning True if the date
                     should be included.
     """
     incr = timedelta(incr, 0, 0)
     while begin <= end:
-        if include is None or include(begin): 
+        if include is None or include(begin):
             yield begin
         begin += incr
 
 def ustrftime(date, fmt='%Y-%m-%d'):
     """like strftime, but returns a unicode string instead of an encoded
     string which may be problematic with localized date.
-    
+
     encoding is guessed by locale.getpreferredencoding()
     """
     # date format may depend on the locale
@@ -79,7 +97,7 @@
     dict1 = dict(dict1)
     dict1.update(dict2)
     return dict1
-                
+
 
 class SizeConstrainedList(list):
     """simple list that makes sure the list does not get bigger
@@ -120,12 +138,12 @@
 
     def __nonzero__(self):
         return True
-    
+
     def write(self, value):
         assert isinstance(value, unicode), u"unicode required not %s : %s"\
                                      % (type(value).__name__, repr(value))
         self.append(value)
-        
+
     def getvalue(self):
         return u''.join(self)
 
@@ -164,8 +182,8 @@
         self.add_post_inline_script(u"""jQuery(document).ready(function () {
  %s
  });""" % jscode)
-        
-    
+
+
     def add_js(self, jsfile):
         """adds `jsfile` to the list of javascripts used in the webpage
 
@@ -231,18 +249,18 @@
         if skiphead:
             return header
         return u'<head>\n%s</head>\n' % header
-        
+
 
 class HTMLStream(object):
     """represents a HTML page.
 
     This is used my main templates so that HTML headers can be added
     at any time during the page generation.
-    
+
     HTMLStream uses the (U)StringIO interface to be compliant with
     existing code.
     """
-    
+
     def __init__(self, req):
         # stream for <head>
         self.head = req.html_headers
--- a/web/form.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/web/form.py	Thu Apr 30 08:51:54 2009 +0200
@@ -75,9 +75,9 @@
         # method on successful commit
         forminfo = self.req.get_session_data(sessionkey, pop=True)
         if forminfo:
-            req.data['formvalues'] = forminfo['values']
-            req.data['formerrors'] = errex = forminfo['errors']
-            req.data['displayederrors'] = set()
+            self.req.data['formvalues'] = forminfo['values']
+            self.req.data['formerrors'] = errex = forminfo['errors']
+            self.req.data['displayederrors'] = set()
             # if some validation error occured on entity creation, we have to
             # get the original variable name from its attributed eid
             foreid = errex.entity
--- a/web/formfields.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/web/formfields.py	Thu Apr 30 08:51:54 2009 +0200
@@ -180,14 +180,13 @@
     def __init__(self, max_length=None, **kwargs):
         super(StringField, self).__init__(**kwargs)
         self.max_length = max_length
+        if max_length < 513 and isinstance(self.widget, TextArea):
+            self.widget.attrs['cols'], self.widget.attrs['rows'] = 60, 5
 
 
-class TextField(Field):
+class TextField(StringField):
+    """XXX string field not enough?"""
     widget = TextArea
-    def __init__(self, rows=10, cols=80, **kwargs):
-        super(TextField, self).__init__(**kwargs)
-        self.rows = rows
-        self.cols = cols
 
 
 class RichTextField(TextField):
@@ -427,7 +426,7 @@
         if isinstance(cstr, SizeConstraint) and cstr.max is not None:
             if cstr.max > 257:
                 rows_cols_from_constraint(cstr, kwargs)
-                field = TextField(**kwargs)
+                field = TextField(max_length=cstr.max, **kwargs)
             else:
                 field = StringField(max_length=cstr.max, **kwargs)
     return field or TextField(**kwargs)
--- a/web/formwidgets.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/web/formwidgets.py	Thu Apr 30 08:51:54 2009 +0200
@@ -137,6 +137,8 @@
     def render(self, form, field):
         name, values, attrs = self._render_attrs(form, field)
         attrs.setdefault('onkeypress', 'autogrow(this)')
+        attrs.setdefault('cols', 80)
+        attrs.setdefault('rows', 20)
         if not values:
             value = u''
         elif len(values) == 1:
--- a/web/uicfg.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/web/uicfg.py	Thu Apr 30 08:51:54 2009 +0200
@@ -12,6 +12,48 @@
 
 from cubicweb.rtags import RelationTags, RelationTagsSet
 
+# primary view configuration ##################################################
+
+# how to display a relation in primary view.
+# values a dict with the following keys:
+#
+# 'where', whose value may be one of:
+#  * 'attributes', display in the attributes section
+#  * 'relations', display in the relations section (below attributes)
+#  * 'sideboxes', display in the side boxes (beside attributes)
+# if this key is missing, the relation won't be displayed at all.
+#
+# 'vid' is an optional view identifier
+#
+# 'label' is an optional label
+#
+# 'limit' is a boolean telling if the results should be limited according to
+#  the configuration
+class RDisplayRelationTags(RelationTags):
+    def __init__(self):
+        super(RDisplayRelationTags, self).__init__()
+        self._counter = 0
+
+    def tag_relation(self, values, *args, **kwargs):
+        super(RDisplayRelationTags, self).tag_relation(values, *args, **kwargs)
+        if values:
+            values['order'] = self.get_timestamp()
+
+    def get_timestamp(self):
+        self._counter += 1
+        return self._counter
+
+rdisplay = RDisplayRelationTags()
+for rtype in ('eid', 'creation_date', 'modification_date',
+              'is', 'is_instance_of', 'identity',
+              'owned_by', 'created_by',
+              'in_state', 'wf_info_for', 'require_permission',
+              'from_entity', 'to_entity',
+              'see_also'):
+    rdisplay.tag_relation({}, ('*', rtype, '*'), 'subject')
+    rdisplay.tag_relation({}, ('*', rtype, '*'), 'object')
+
+
 # autoform.AutomaticEntityForm configuration ##################################
 
 # relations'category (eg primary/secondary/generic/metadata/generated)
--- a/web/views/actions.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/web/views/actions.py	Thu Apr 30 08:51:54 2009 +0200
@@ -154,7 +154,7 @@
 
     title = _('manage permissions')
     category = 'moreactions'
-    order = 100
+    order = 15
 
     @classmethod
     def registered(cls, vreg):
@@ -248,7 +248,7 @@
     order = 20
 
     def url(self):
-        return self.build_url('euser/%s'%self.req.user.login, vid='edition')
+        return self.build_url('cwuser/%s'%self.req.user.login, vid='edition')
 
 
 class LogoutAction(Action):
--- a/web/views/basecomponents.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/web/views/basecomponents.py	Thu Apr 30 08:51:54 2009 +0200
@@ -16,7 +16,7 @@
 from cubicweb.schema import display_name
 
 from cubicweb.web.htmlwidgets import MenuWidget, PopupBoxMenu, BoxSeparator, BoxLink
-from cubicweb.web.component import Component, RelatedObjectsVComponent
+from cubicweb.web import uicfg, component
 
 _ = unicode
 
@@ -25,7 +25,7 @@
                         help=_('display the component or not')),
     }
 
-class RQLInputForm(Component):
+class RQLInputForm(component.Component):
     """build the rql input form, usually displayed in the header"""
     id = 'rqlinput'
     property_defs = VISIBLE_PROP_DEF
@@ -54,7 +54,7 @@
         self.w(u'</form></div>')
 
 
-class ApplLogo(Component):
+class ApplLogo(component.Component):
     """build the application logo, usually displayed in the header"""
     id = 'logo'
     property_defs = VISIBLE_PROP_DEF
@@ -66,7 +66,7 @@
                % (self.req.base_url(), self.req.external_resource('LOGO')))
 
 
-class ApplHelp(Component):
+class ApplHelp(component.Component):
     """build the help button, usually displayed in the header"""
     id = 'help'
     property_defs = VISIBLE_PROP_DEF
@@ -76,7 +76,7 @@
                   self.req._(u'help'),))
 
 
-class UserLink(Component):
+class UserLink(component.Component):
     """if the user is the anonymous user, build a link to login
     else a link to the connected user object with a loggout link
     """
@@ -120,7 +120,7 @@
                    % (self.build_url('login'), self.req._('login')))
 
 
-class ApplicationMessage(Component):
+class ApplicationMessage(component.Component):
     """display application's messages given using the __message parameter
     into a special div section
     """
@@ -141,7 +141,7 @@
         self.w(u'</div>')
 
 
-class ApplicationName(Component):
+class ApplicationName(component.Component):
     """display the application name"""
     id = 'appliname'
     property_defs = VISIBLE_PROP_DEF
@@ -151,19 +151,22 @@
                                                          self.req.property_value('ui.site-title')))
 
 
-class SeeAlsoVComponent(RelatedObjectsVComponent):
+uicfg.rdisplay.tag_relation({}, ('*', 'see_also', '*'), 'subject')
+uicfg.rdisplay.tag_relation({}, ('*', 'see_also', '*'), 'object')
+
+class SeeAlsoVComponent(component.RelatedObjectsVComponent):
     """display any entity's see also"""
     id = 'seealso'
     context = 'navcontentbottom'
     rtype = 'see_also'
-    target = 'object'
+    role = 'subject'
     order = 40
     # register msg not generated since no entity use see_also in cubicweb itself
     title = _('contentnavigation_seealso')
     help = _('contentnavigation_seealso_description')
 
 
-class EtypeRestrictionComponent(Component):
+class EtypeRestrictionComponent(component.Component):
     """displays the list of entity types contained in the resultset
     to be able to filter accordingly.
     """
--- a/web/views/baseviews.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/web/views/baseviews.py	Thu Apr 30 08:51:54 2009 +0200
@@ -165,7 +165,7 @@
             self.w(u'#%s - ' % entity.eid)
         if entity.modification_date != entity.creation_date:
             self.w(u'<span>%s</span> ' % _('latest update on'))
-            self.w(u'<span class="value">%s</span>, ;'
+            self.w(u'<span class="value">%s</span>, '
                    % self.format_date(entity.modification_date))
         # entities from external source may not have a creation date (eg ldap)
         if entity.creation_date:
@@ -373,7 +373,6 @@
 
 from cubicweb.web.views import boxes, xmlrss, primary
 PrimaryView = class_moved(primary.PrimaryView)
-PRIMARY_SKIP_RELS = primary.PRIMARY_SKIP_RELS
 SideBoxView = class_moved(boxes.SideBoxView)
 XmlView = class_moved(xmlrss.XmlView)
 XmlItemView = class_moved(xmlrss.XmlItemView)
--- a/web/views/cwuser.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/web/views/cwuser.py	Thu Apr 30 08:51:54 2009 +0200
@@ -26,6 +26,8 @@
 uicfg.rmode.tag_relation('link', ('*', 'owned_by', 'CWUser'), 'object')
 uicfg.rmode.tag_relation('link', ('*', 'created_by', 'CWUser'), 'object')
 uicfg.rmode.tag_relation('create', ('*', 'bookmarked_by', 'CWUser'), 'object')
+uicfg.rdisplay.tag_attribute({}, 'CWUser', 'firstname')
+uicfg.rdisplay.tag_attribute({}, 'CWUser', 'surname')
 
 
 class UserPreferencesEntityAction(action.Action):
@@ -38,29 +40,15 @@
 
     def url(self):
         login = self.rset.get_entity(self.row or 0, self.col or 0).login
-        return self.build_url('euser/%s'%login, vid='epropertiesform')
+        return self.build_url('cwuser/%s'%login, vid='epropertiesform')
 
 
 class CWUserPrimaryView(PrimaryView):
     __select__ = implements('CWUser')
 
-    skip_attrs = ('firstname', 'surname')
-
-    def iter_relations(self, entity):
-        # don't want to display user's entities
-        for rschema, targetschemas, x in super(CWUserPrimaryView, self).iter_relations(entity):
-            if x == 'object' and rschema.type in ('owned_by', 'for_user'):
-                continue
-            yield rschema, targetschemas, x
-
     def content_title(self, entity):
         return entity.name()
 
-    def is_side_related(self, rschema, eschema):
-        # XXX only bookmarked_by defined in cw...
-        return  rschema.type in ['interested_in', 'tags',
-                                 'todo_by', 'bookmarked_by']
-
 
 class FoafView(EntityView):
     id = 'foaf'
--- a/web/views/emailaddress.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/web/views/emailaddress.py	Thu Apr 30 08:51:54 2009 +0200
@@ -20,7 +20,7 @@
         self.skipeids = skipeids
         super(EmailAddressPrimaryView, self).cell_call(row, col)
 
-    def render_entity_attributes(self, entity, siderelations):
+    def render_entity_attributes(self, entity):
         self.w(u'<h3>')
         entity.view('oneline', w=self.w)
         if not entity.canonical:
@@ -53,7 +53,7 @@
             emailofstr = ', '.join(e.view('oneline') for e in emailof)
             self.field(display_name(self.req, 'use_email', 'object'), emailofstr)
 
-    def render_entity_relations(self, entity, siderelations):
+    def render_entity_relations(self, entity):
         for i, email in enumerate(entity.related_emails(self.skipeids)):
             self.w(u'<div class="%s">' % (i%2 and 'even' or 'odd'))
             email.view('oneline', w=self.w, contexteid=entity.eid)
@@ -64,7 +64,8 @@
     __select__ = implements('EmailAddress')
     id = 'shortprimary'
     title = None # hidden view
-    def render_entity_attributes(self, entity, siderelations):
+    
+    def render_entity_attributes(self, entity):
         self.w(u'<h5>')
         entity.view('oneline', w=self.w)
         self.w(u'</h5>')
--- a/web/views/idownloadable.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/web/views/idownloadable.py	Thu Apr 30 08:51:54 2009 +0200
@@ -94,19 +94,11 @@
         self.w(u'<a href="%s">%s</a>' % (url, html_escape(title or entity.dc_title())))
 
 
-
 class IDownloadablePrimaryView(baseviews.PrimaryView):
     __select__ = implements(IDownloadable)
-    # XXX File/Image attributes but this is not specified in the IDownloadable interface
-    skip_attrs = baseviews.PrimaryView.skip_attrs + ('data', 'name')
 
-    def render_entity_title(self, entity):
-        self.w(u'<h1>%s %s</h1>'
-               % (entity.dc_type().capitalize(),
-                  html_escape(entity.dc_title())))
-
-    def render_entity_attributes(self, entity, siderelations):
-        super(IDownloadablePrimaryView, self).render_entity_attributes(entity, siderelations)
+    def render_entity_attributes(self, entity):
+        super(IDownloadablePrimaryView, self).render_entity_attributes(entity)
         self.w(u'<div class="content">')
         contenttype = entity.download_content_type()
         if contenttype.startswith('image/'):
@@ -123,10 +115,6 @@
                 self.w('<div class="error">%s</div>' % msg)
         self.w(u'</div>')
 
-    def is_side_related(self, rschema, eschema):
-        """display all relations as side related"""
-        return True
-
 
 class IDownloadableLineView(baseviews.OneLineView):
     __select__ = implements(IDownloadable)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/views/isioc.py	Thu Apr 30 08:51:54 2009 +0200
@@ -0,0 +1,82 @@
+"""Specific views for SIOC interfaces
+
+:organization: Logilab
+:copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from logilab.mtconverter import html_escape
+
+from cubicweb.view import EntityView
+from cubicweb.selectors import implements
+from cubicweb.interfaces import ISiocItem, ISiocContainer
+
+class SIOCView(EntityView):
+    id = 'sioc'
+    __select__ = EntityView.__select__ & implements(ISiocItem, ISiocContainer)
+    title = _('sioc')
+    templatable = False
+    content_type = 'text/xml'
+
+    def call(self):
+        self.w(u'<?xml version="1.0" encoding="%s"?>\n' % self.req.encoding)
+        self.w(u'''<rdf:RDF
+             xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+             xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
+             xmlns:owl="http://www.w3.org/2002/07/owl#"
+             xmlns:foaf="http://xmlns.com/foaf/0.1/"
+             xmlns:sioc="http://rdfs.org/sioc/ns#"
+             xmlns:sioctype="http://rdfs.org/sioc/types#"
+             xmlns:dcterms="http://purl.org/dc/terms/">\n''')
+        for i in xrange(self.rset.rowcount):
+            self.cell_call(i, 0)
+        self.w(u'</rdf:RDF>\n')
+
+    def cell_call(self, row, col):
+        self.wview('sioc_element', self.rset, row=row, col=col)
+
+class SIOCContainerView(EntityView):
+    id = 'sioc_element'
+    __select__ = EntityView.__select__ & implements(ISiocContainer)
+    templatable = False
+    content_type = 'text/xml'
+
+    def cell_call(self, row, col):
+        entity = self.complete_entity(row, col)
+        self.w(u'<sioc:%s rdf:about="%s">\n' % (html_escape(entity.isioc_type()),
+                                                html_escape(entity.absolute_url())))
+        self.w(u'<dcterms:title>%s</dcterms:title>' % html_escape(entity.dc_title()))
+        self.w(u'<dcterms:created>%s</dcterms:created>' % entity.creation_date)
+        self.w(u'<dcterms:modified>%s</dcterms:modified>' % entity.modification_date)
+        self.w(u'<!-- FIXME : here be items -->')#entity.isioc_items()
+        self.w(u'</sioc:%s>\n' % entity.isioc_type())
+
+
+class SIOCItemView(EntityView):
+    id = 'sioc_element'
+    __select__ = EntityView.__select__ & implements(ISiocItem)
+    templatable = False
+    content_type = 'text/xml'
+
+    def cell_call(self, row, col):
+        entity = self.complete_entity(row, col)
+        self.w(u'<sioc:%s rdf:about="%s">\n' %  (html_escape(entity.isioc_type()),
+                                                 html_escape(entity.absolute_url())))
+        self.w(u'<dcterms:title>%s</dcterms:title>' % html_escape(entity.dc_title()))
+        self.w(u'<dcterms:created>%s</dcterms:created>' % entity.creation_date)
+        self.w(u'<dcterms:modified>%s</dcterms:modified>' % entity.modification_date)
+        if entity.content:
+            self.w(u'<sioc:content>%s</sioc:content>''' % html_escape(entity.isioc_content()))
+        if entity.related('entry_of'):
+            self.w(u'<sioc:has_container rdf:resource="%s"/>\n' % html_escape(entity.isioc_container().absolute_url()))
+        if entity.creator:
+            self.w(u'<sioc:has_creator>\n')
+            self.w(u'<sioc:User rdf:about="%s">\n' % html_escape(entity.creator.absolute_url()))
+            self.w(entity.creator.view('foaf'))
+            self.w(u'</sioc:User>\n')
+            self.w(u'</sioc:has_creator>\n')
+        self.w(u'<!-- FIXME : here be topics -->')#entity.isioc_topics()
+        self.w(u'<!-- FIXME : here be replies -->')#entity.isioc_replies()
+        self.w(u' </sioc:%s>\n' % 'Post')
+
--- a/web/views/primary.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/web/views/primary.py	Thu Apr 30 08:51:54 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,16 +82,16 @@
         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:
-            self.w(u'<table width="100%"><tr><td width="75%">')
+        boxes = self._prepare_side_boxes(entity)
+        if boxes or hasattr(self, 'render_side_related'):
+            self.w(u'<table width="100%"><tr><td style="width: 75%">')
         self.w(u'<div>')
         self.w(u'<div class="mainInfo">')
         try:
             self.render_entity_attributes(entity)
         except TypeError: # XXX bw compat
             warn('siderelations argument of render_entity_attributes is '
-                 'deprecated')
+                 'deprecated (%s)' % self.__class__)
             self.render_entity_attributes(entity, [])
         self.w(u'</div>')
         self.content_navigation_components('navcontenttop')
@@ -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,38 @@
         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() or vid == 'reledit':
+                value = entity.view(vid, rtype=rschema.type, role=role)
             else:
-                value = wdg.render(entity)
+                rset = self._relation_rset(entity, rschema, role, displayinfo)
+                if rset:
+                    value = self.view(vid, rset)
+                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 +190,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 +269,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),
--- a/web/views/schema.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/web/views/schema.py	Thu Apr 30 08:51:54 2009 +0200
@@ -14,9 +14,9 @@
 from cubicweb.selectors import implements, yes
 from cubicweb.schemaviewer import SchemaViewer
 from cubicweb.view import EntityView, StartupView
-from cubicweb.common.uilib import ureport_as_html
+from cubicweb.common import tags, uilib
 from cubicweb.web import uicfg, action
-from cubicweb.web.views import TmpFileViewMixin, baseviews
+from cubicweb.web.views import TmpFileViewMixin, primary, baseviews
 
 
 uicfg.rcategories.tag_relation('primary', ('CWPermission', 'require_group', '*'), 'subject')
@@ -33,6 +33,11 @@
 uicfg.rmode.tag_relation('link', ('*', 'from_entity', 'CWEType'), 'object')
 uicfg.rmode.tag_relation('link', ('*', 'to_entity', 'CWEType'), 'object')
 
+for attr in ('name', 'meta', 'final'):
+    uicfg.rdisplay.tag_attribute({}, 'CWRType', attr)
+for attr in ('name', 'meta', 'final', 'symetric', 'inlined'):
+    uicfg.rdisplay.tag_attribute({}, 'CWRType', attr)
+
 
 class ViewSchemaAction(action.Action):
     id = 'schema'
@@ -48,25 +53,13 @@
 
 # schema entity types views ###################################################
 
-class _SchemaEntityPrimaryView(baseviews.PrimaryView):
-    show_attr_label = False
+class CWRDEFPrimaryView(primary.PrimaryView):
+    __select__ = implements('CWAttribute', 'CWRelation')
     cache_max_age = 60*60*2 # stay in http cache for 2 hours by default
 
     def content_title(self, entity):
         return html_escape(entity.dc_long_title())
 
-class CWETypePrimaryView(_SchemaEntityPrimaryView):
-    __select__ = implements('CWEType')
-    skip_attrs = _SchemaEntityPrimaryView.skip_attrs + ('name', 'meta', 'final')
-
-class CWRTypePrimaryView(_SchemaEntityPrimaryView):
-    __select__ = implements('CWRType')
-    skip_attrs = _SchemaEntityPrimaryView.skip_attrs + ('name', 'meta', 'final',
-                                                        'symetric', 'inlined')
-
-class ErdefPrimaryView(_SchemaEntityPrimaryView):
-    __select__ = implements('CWAttribute', 'CWRelation')
-    show_attr_label = True
 
 class CWETypeOneLineView(baseviews.OneLineView):
     __select__ = implements('CWEType')
@@ -82,41 +75,44 @@
 
 
 # in memory schema views (yams class instances) ###############################
+SKIPPED_RELS = ('is', 'is_instance_of', 'identity', 'created_by', 'owned_by',
+                'has_text',)
 
-class CWETypeSchemaView(CWETypePrimaryView):
+class CWETypeSchemaView(primary.PrimaryView):
     id = 'eschema'
+    __select__ = implements('CWEType')
     title = _('in memory entity schema')
     main_related_section = False
-    skip_rels = ('is', 'is_instance_of', 'identity', 'created_by', 'owned_by',
-                 'has_text',)
+    skip_rels = SKIPPED_RELS
 
-    def render_entity_attributes(self, entity, siderelations):
-        super(CWETypeSchemaView, self).render_entity_attributes(entity, siderelations)
+    def render_entity_attributes(self, entity):
+        super(CWETypeSchemaView, self).render_entity_attributes(entity)
         eschema = self.vreg.schema.eschema(entity.name)
         viewer = SchemaViewer(self.req)
         layout = viewer.visit_entityschema(eschema, skiprels=self.skip_rels)
-        self.w(ureport_as_html(layout))
+        self.w(uilib.ureport_as_html(layout))
         if not eschema.is_final():
-            self.w(u'<img src="%s" alt="%s"/>' % (
-                html_escape(entity.absolute_url(vid='eschemagraph')),
-                html_escape(self.req._('graphical schema for %s') % entity.name)))
+            msg = self.req._('graphical schema for %s') % entity.name
+            self.w(tags.img(src=entity.absolute_url(vid='eschemagraph'),
+                            alt=msg))
 
 
-class CWRTypeSchemaView(CWRTypePrimaryView):
+class CWRTypeSchemaView(primary.PrimaryView):
     id = 'eschema'
+    __select__ = implements('CWRType')
     title = _('in memory relation schema')
     main_related_section = False
 
-    def render_entity_attributes(self, entity, siderelations):
-        super(CWRTypeSchemaView, self).render_entity_attributes(entity, siderelations)
+    def render_entity_attributes(self, entity):
+        super(CWRTypeSchemaView, self).render_entity_attributes(entity)
         rschema = self.vreg.schema.rschema(entity.name)
         viewer = SchemaViewer(self.req)
         layout = viewer.visit_relationschema(rschema)
-        self.w(ureport_as_html(layout))
+        self.w(uilib.ureport_as_html(layout))
         if not rschema.is_final():
-            self.w(u'<img src="%s" alt="%s"/>' % (
-                html_escape(entity.absolute_url(vid='eschemagraph')),
-                html_escape(self.req._('graphical schema for %s') % entity.name)))
+            msg = self.req._('graphical schema for %s') % entity.name
+            self.w(tags.img(src=entity.absolute_url(vid='eschemagraph'),
+                            alt=msg))
 
 
 # schema images ###############################################################
@@ -198,8 +194,9 @@
 
 class SchemaImageView(TmpFileViewMixin, StartupView):
     id = 'schemagraph'
+
     content_type = 'image/png'
-    skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of')
+    skip_rels = SKIPPED_RELS
     def _generate(self, tmpfile):
         """display global schema information"""
         skipmeta = not int(self.req.form.get('withmeta', 0))
@@ -209,9 +206,10 @@
 
 class CWETypeSchemaImageView(TmpFileViewMixin, EntityView):
     id = 'eschemagraph'
+    __select__ = implements('CWEType')
+
     content_type = 'image/png'
-    __select__ = implements('CWEType')
-    skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of')
+    skip_rels = SKIPPED_RELS
 
     def _generate(self, tmpfile):
         """display schema information for an entity"""
--- a/web/views/timetable.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/web/views/timetable.py	Thu Apr 30 08:51:54 2009 +0200
@@ -45,7 +45,7 @@
                 user = u"*"
             the_dates = []
             if task.start and task.stop:
-                if task.start.absdate == task.stop.absdate:
+                if task.start.toordinal() == task.stop.toordinal():
                     the_dates.append(task.start)
                 else:
                     the_dates += date_range( task.start, task.stop )
--- a/web/views/xmlrss.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/web/views/xmlrss.py	Thu Apr 30 08:51:54 2009 +0200
@@ -155,11 +155,11 @@
         self.w(u'<?xml version="1.0" encoding="%s"?>\n' % req.encoding)
         self.w(u'<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">\n')
         self.w(u'  <channel>\n')
-        self.w(u'    <title>%s RSS Feed</title>\n' % html_escape(self.page_title()))
-        self.w(u'    <description>%s</description>\n' % html_escape(req.form.get('vtitle', '')))
+        self.w(u'    <title>%s RSS Feed</title>\n' % xml_escape(self.page_title()))
+        self.w(u'    <description>%s</description>\n' % xml_escape(req.form.get('vtitle', '')))
         params = req.form.copy()
         params.pop('vid', None)
-        self.w(u'    <link>%s</link>\n' % html_escape(self.build_url(**params)))
+        self.w(u'    <link>%s</link>\n' % xml_escape(self.build_url(**params)))
 
     def _close(self):
         self.w(u'  </channel>\n')
@@ -175,6 +175,7 @@
     def cell_call(self, row, col):
         self.wview('rssitem', self.rset, row=row, col=col)
 
+
 class RssItemView(EntityView):
     id = 'rssitem'
     date_format = '%%Y-%%m-%%dT%%H:%%M%+03i:00' % (timezone / 3600)
@@ -183,9 +184,9 @@
     def cell_call(self, row, col):
         entity = self.complete_entity(row, col)
         self.w(u'<item>\n')
-        self.w(u'<guid isPermaLink="true">%s</guid>\n' % html_escape(entity.absolute_url()))
+        self.w(u'<guid isPermaLink="true">%s</guid>\n' % xml_escape(entity.absolute_url()))
         self.render_title_link(entity)
-        self._marker('description', html_escape(entity.dc_description()))
+        self._marker('description', xml_escape(entity.dc_description()))
         self._marker('dc:date', entity.dc_date(self.date_format))
         self.render_entity_creator(entity)
         self.w(u'</item>\n')
@@ -201,4 +202,4 @@
 
     def _marker(self, marker, value):
         if value:
-            self.w(u'  <%s>%s</%s>\n' % (marker, html_escape(value), marker))
+            self.w(u'  <%s>%s</%s>\n' % (marker, xml_escape(value), marker))
--- a/web/widgets.py	Wed Apr 29 19:54:50 2009 +0200
+++ b/web/widgets.py	Thu Apr 30 08:51:54 2009 +0200
@@ -63,7 +63,7 @@
     autoid = True
     html_attributes = set(('id', 'class', 'tabindex', 'accesskey', 'onchange', 'onkeypress'))
     cubicwebns_attributes = set()
-    
+
     def __init__(self, vreg, subjschema, rschema, objschema,
                  role='subject', description=None,
                  **kwattrs):
@@ -83,12 +83,12 @@
         because widget instances are cached)
         """
         # brute force copy (subclasses don't have the
-        # same __init__ prototype) 
+        # same __init__ prototype)
         widget = self.__new__(self.__class__)
         widget.__dict__ = dict(self.__dict__)
         widget.attrs = dict(widget.attrs)
         return widget
-    
+
     @staticmethod
     def size_constraint_attrs(attrs, maxsize):
         """set html attributes in the attrs dict to consider maxsize"""
@@ -105,7 +105,7 @@
             elif name in self.html_attributes:
                 attrs.append(u'%s="%s"' % (name, value))
         return u' '.join(sorted(attrs))
-        
+
     def required(self, entity):
         """indicates if the widget needs a value to be filled in"""
         card = self.rschema.cardinality(self.subjtype, self.objtype, self.role)
@@ -116,7 +116,7 @@
             return self.rname
         except AttributeError:
             return eid_param(self.name, entity.eid)
-    
+
     def render_label(self, entity, label=None):
         """render widget's label"""
         label = label or self.rschema.display_name(entity.req, self.role)
@@ -130,7 +130,7 @@
         else:
             label = u'<label%s>%s</label>' % (forattr, label)
         return label
-    
+
     def render_error(self, entity):
         """return validation error for widget's field of the given entity, if
         any
@@ -153,16 +153,16 @@
             help.append(u'<span class="helper">(%s: %s)</span>'
                         % (req._('sample format'), example))
         return u'&nbsp;'.join(help)
-    
+
     def render_example(self, req):
         return u''
-        
+
     def render(self, entity):
         """render the widget for a simple view"""
         if not entity.has_eid():
             return u''
         return entity.printable_value(self.name)
-    
+
     def edit_render(self, entity, tabindex=None,
                     includehelp=False, useid=None, **kwargs):
         """render the widget for edition"""
@@ -180,7 +180,7 @@
         if includehelp:
             output += self.render_help(entity)
         return output
-    
+
     def _edit_render(self, entity):
         """do the actual job to render the widget for edition"""
         raise NotImplementedError
@@ -196,7 +196,7 @@
         elif entity.has_eid():
             return [row[0] for row in entity.related(self.name, self.role)]
         return ()
-            
+
     def current_value(self, entity):
         return _value_from_values(self.current_values(entity))
 
@@ -213,13 +213,13 @@
         if not isinstance(cdvalues, (list, tuple)):
             cdvalues = (cdvalues,)
         return cdvalues
-    
+
     def current_display_value(self, entity):
         """same as .current_value but consider values stored in session in case
         of validation error
         """
         return _value_from_values(self.current_display_values(entity))
-    
+
     def hidden_input(self, entity, qvalue):
         """return an hidden field which
         1. indicates that a field is edited
@@ -258,7 +258,7 @@
     def __init__(self, vreg, subjschema, rschema, objschema,
                  role='subject', **kwattrs):
         InputWidget.__init__(self, vreg, subjschema, rschema, objschema,
-                             role='subject', 
+                             role='subject',
                              **kwattrs)
         # disable access key
         del self.attrs['accesskey']
@@ -270,18 +270,18 @@
     def current_display_value(self, entity):
         value = InputWidget.current_display_value(self, entity)
         return value or INTERNAL_FIELD_VALUE
-    
+
     def render_label(self, entity, label=None):
         """render widget's label"""
         return u''
-    
+
     def render_help(self, entity):
         return u''
-    
+
     def hidden_input(self, entity, value):
         """no hidden input for hidden input"""
         return ''
-    
+
 
 class EidWidget(HiddenWidget):
 
@@ -297,15 +297,15 @@
         """set html attributes in the attrs dict to consider maxsize"""
         attrs['size'] = min(maxsize, 40)
         attrs['maxlength'] = maxsize
-        
-        
+
+
 class AutoCompletionWidget(StringWidget):
     cubicwebns_attributes = (StringWidget.cubicwebns_attributes |
                           set(('accesskey', 'size', 'maxlength')))
     attrs = ()
-    
+
     wdgtype = 'SuggestField'
-    
+
     def current_value(self, entity):
         value = StringWidget.current_value(self, entity)
         return value or INTERNAL_FIELD_VALUE
@@ -344,22 +344,22 @@
 
 class StaticFileAutoCompletionWidget(AutoCompletionWidget):
     wdgtype = 'StaticFileSuggestField'
-    
+
     def _get_url(self, entity):
         return entity.req.datadir_url + entity.autocomplete_initfuncs[self.rschema]
 
 class RestrictedAutoCompletionWidget(AutoCompletionWidget):
-    wdgtype = 'RestrictedSuggestField'    
+    wdgtype = 'RestrictedSuggestField'
 
-    
+
 class PasswordWidget(InputWidget):
     input_type = 'password'
-    
+
     def required(self, entity):
         if InputWidget.required(self, entity) and not entity.has_eid():
             return True
         return False
-    
+
     def current_values(self, entity):
         # on existant entity, show password field has non empty (we don't have
         # the actual value
@@ -374,10 +374,10 @@
             html, self.input_type, name, name, entity.req.next_tabindex(),
             entity.req._('confirm password'))
 
-    
+
 class TextWidget(Widget):
     html_attributes = Widget.html_attributes | set(('rows', 'cols'))
-    
+
     @staticmethod
     def size_constraint_attrs(attrs, maxsize):
         """set html attributes in the attrs dict to consider maxsize"""
@@ -385,12 +385,12 @@
             attrs['cols'], attrs['rows'] = 60, 5
         else:
             attrs['cols'], attrs['rows'] = 80, 10
-    
+
     def render(self, entity):
         if not entity.has_eid():
             return u''
         return entity.printable_value(self.name)
-        
+
     def _edit_render(self, entity, with_format=True):
         req = entity.req
         editor = self._edit_render_textarea(entity, with_format)
@@ -398,7 +398,7 @@
         if isinstance(value, basestring):
             value = html_escape(value)
         return u'%s%s' % (self.hidden_input(entity, value), editor)
-    
+
     def _edit_render_textarea(self, entity, with_format):
         self.attrs.setdefault('cols', 80)
         self.attrs.setdefault('rows', 20)
@@ -426,8 +426,8 @@
             fmtwdgstr = ''
         return u'%s<br/><textarea onkeypress="autogrow(this)" name="%s" %s>%s</textarea>' % (
             fmtwdgstr, self.rname, self.format_attrs(), dvalue)
-            
-    
+
+
 class CheckBoxWidget(Widget):
     html_attributes = Widget.html_attributes | set(('checked', ))
     def _edit_render(self, entity):
@@ -460,7 +460,7 @@
                 u'<input type="radio" name="%s" value="" %s/>%s<br/>' % (self.rname, attrs2, entity.req._('no'))]
         return '\n'.join(wdgs)
 
-    
+
 class FileWidget(Widget):
     need_multipart = True
     def _file_wdg(self, entity):
@@ -492,7 +492,7 @@
                 wdgs.append(u'<br/>')
                 wdgs.append(req._('currently attached file: %s' % entity.dc_title()))
         return '\n'.join(wdgs)
-    
+
     def _edit_render(self, entity):
         return self.hidden_input(entity, None) + self._file_wdg(entity)
 
@@ -510,7 +510,7 @@
                 'You can either submit a new file using the browse button above'
                 ', or edit file content online with the widget below.')
         return msg
-    
+
     def _edit_render(self, entity):
         wdgs = [self._file_wdg(entity)]
         if entity.attr_metadata(self.name, 'format') in ('text/plain', 'text/html', 'text/rest'):
@@ -534,7 +534,7 @@
 
 class ComboBoxWidget(Widget):
     html_attributes = Widget.html_attributes | set(('multiple', 'size'))
-    
+
     def __init__(self, vreg, subjschema, rschema, objschema,
                  multiple=False, **kwattrs):
         super(ComboBoxWidget, self).__init__(vreg, subjschema, rschema, objschema,
@@ -545,10 +545,10 @@
                 self.attrs['size'] = '5'
         # disable access key (dunno why but this is not allowed by xhtml 1.0)
         del self.attrs['accesskey']
-        
+
     def vocabulary(self, entity):
         raise NotImplementedError()
-    
+
     def form_value(self, entity, value, values):
         if value in values:
             flag = 'selected="selected"'
@@ -574,9 +574,9 @@
         res.append(u'</select>')
         return '\n'.join(res)
 
- 
+
 class StaticComboBoxWidget(ComboBoxWidget):
-    
+
     def __init__(self, vreg, subjschema, rschema, objschema,
                  vocabfunc, multiple=False, sort=False, **kwattrs):
         super(StaticComboBoxWidget, self).__init__(vreg, subjschema, rschema, objschema,
@@ -591,11 +591,11 @@
         if self.rschema.rproperty(self.subjtype, self.objtype, 'internationalizable'):
             return zip((entity.req._(v) for v in choices), choices)
         return zip(choices, choices)
-    
+
 
 class EntityLinkComboBoxWidget(ComboBoxWidget):
     """to be used be specific forms"""
-    
+
     def current_values(self, entity):
         if entity.has_eid():
             return [r[0] for r in entity.related(self.name, self.role)]
@@ -603,13 +603,13 @@
         if hasattr(entity, defaultmeth):
             return getattr(entity, defaultmeth)()
         return ()
-    
+
     def vocabulary(self, entity):
         return [('', INTERNAL_FIELD_VALUE)] + entity.vocabulary(self.rschema, self.role)
 
 
 class RawDynamicComboBoxWidget(EntityLinkComboBoxWidget):
-    
+
     def vocabulary(self, entity, limit=None):
         req = entity.req
         # first see if its specified by __linkto form parameters
@@ -632,7 +632,7 @@
 
 
 class DynamicComboBoxWidget(RawDynamicComboBoxWidget):
-    
+
     def vocabulary(self, entity, limit=None):
         return sorted(super(DynamicComboBoxWidget, self).vocabulary(entity, limit))
 
@@ -669,11 +669,11 @@
         kwattrs['size'] = 5
         kwattrs['maxlength'] = 15
         StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
-        
+
     def render_example(self, req):
         return '23'
-    
-        
+
+
 class FloatWidget(StringWidget):
     def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
         kwattrs['size'] = 5
@@ -683,7 +683,7 @@
     def render_example(self, req):
         formatstr = req.property_value('ui.float-format')
         return formatstr % 1.23
-    
+
     def current_values(self, entity):
         values = entity.attribute_values(self.name)
         if values:
@@ -702,7 +702,7 @@
         kwattrs['size'] = 5
         kwattrs['maxlength'] = 15
         StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
-        
+
     def render_example(self, req):
         return '345.0300'
 
@@ -724,7 +724,7 @@
         daynames = [_(dname) for dname in cls.daynames]
         req.html_headers.define_var('MONTHNAMES', monthnames)
         req.html_headers.define_var('DAYNAMES', daynames)
-    
+
     def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
         kwattrs.setdefault('size', 10)
         kwattrs.setdefault('maxlength', 10)
@@ -784,7 +784,7 @@
         kwattrs['size'] = 16
         kwattrs['maxlength'] = 16
         DateWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
-    
+
     def render_example(self, req):
         formatstr1 = req.property_value('ui.datetime-format')
         formatstr2 = req.property_value('ui.date-format')
@@ -801,26 +801,26 @@
         kwattrs['maxlength'] = 5
         StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
 
-        
+
 class EmailWidget(StringWidget):
-    
+
     def render(self, entity):
         email = getattr(entity, self.name)
         if not email:
             return u''
         return u'<a href="mailto:%s">%s</a>' % (email, email)
-        
+
 class URLWidget(StringWidget):
-    
+
     def render(self, entity):
         url = getattr(entity, self.name)
         if not url:
             return u''
         url = html_escape(url)
         return u'<a href="%s">%s</a>' % (url, url)
-    
+
 class EmbededURLWidget(StringWidget):
-    
+
     def render(self, entity):
         url = getattr(entity, self.name)
         if not url:
@@ -828,7 +828,7 @@
         aurl = html_escape(entity.build_url('embed', url=url))
         return u'<a href="%s">%s</a>' % (aurl, url)
 
-    
+
 
 def widget_factory(vreg, subjschema, rschema, objschema, role='subject',
                    **kwargs):
@@ -857,14 +857,14 @@
 
 
 # factories to find the most adapated widget according to a type and other constraints
-                
+
 def _string_widget_factory(vreg, subjschema, rschema, objschema, wcls=None, **kwargs):
     w = None
     for c in rschema.rproperty(subjschema, objschema, 'constraints'):
         if isinstance(c, StaticVocabularyConstraint):
             # may have been set by a previous SizeConstraint but doesn't make sense
             # here (even doesn't have the same meaning on a combobox actually)
-            kwargs.pop('size', None) 
+            kwargs.pop('size', None)
             return (wcls or StaticComboBoxWidget)(vreg, subjschema, rschema, objschema,
                                                   vocabfunc=c.vocabulary, **kwargs)
         if isinstance(c, SizeConstraint) and c.max is not None:
@@ -914,7 +914,7 @@
     'String' :  StringWidget,
     'Time':     TimeWidget,
     }
-    
+
 # widgets registry
 WIDGETS = {}
 def register(widget_list):