close tls-sprint branch
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Wed, 03 Jun 2009 09:15:20 +0200
changeset 2023 f190b6681273
parent 2021 1eb93a80c127 (diff)
parent 2022 31412adee482 (current diff)
child 2025 010a4b0fe855
close tls-sprint branch
--- a/.hgtags	Wed Jun 03 09:09:33 2009 +0200
+++ b/.hgtags	Wed Jun 03 09:15:20 2009 +0200
@@ -26,3 +26,15 @@
 9e98dec0768b87363a7826a04636dc161ed0ec7d cubicweb-debian-version-3_1_3-1
 e0e0a1c3d80f4fbf4bbd55066278e467b75df8a4 cubicweb-version-3_1_4
 0e132fbae9cc5e004f4b79a8b842addad43519a7 cubicweb-debian-version-3_1_4-1
+c14231e3a4f9120e2bb6a1d8690252fff5e48131 cubicweb-version-3_2_0
+c9c492787a8aa1b7916e22eb6498cba1c8fa316c cubicweb-debian-version-3_2_0-1
+634c251dd032894850080c4e5aeb0a4e09f888c0 cubicweb-version-3_2_1
+e784f8847a124a93e5b385d7a92a2772c050fe82 cubicweb-debian-version-3_2_1-1
+6539ce84f04357ef65ccee0896a30997b16a4ece cubicweb-version-3_2_2
+92d1a15f08f7c5fa87643ffb4273d12cb3f41c63 cubicweb-debian-version-3_2_2-1
+6539ce84f04357ef65ccee0896a30997b16a4ece cubicweb-version-3_2_2
+9b21e068fef73c37bcb4e53d006a7bde485f390b cubicweb-version-3_2_2
+92d1a15f08f7c5fa87643ffb4273d12cb3f41c63 cubicweb-debian-version-3_2_2-1
+0e07514264aa1b0b671226f41725ea4c066c210a cubicweb-debian-version-3_2_2-1
+f60bb84b86cf371f1f25197e00c778b469297721 cubicweb-version-3_2_3
+4003d24974f15f17bd03b7efd6a5047cad4e4c41 cubicweb-debian-version-3_2_3-1
--- a/MANIFEST.in	Wed Jun 03 09:09:33 2009 +0200
+++ b/MANIFEST.in	Wed Jun 03 09:15:20 2009 +0200
@@ -3,7 +3,7 @@
 include bin/cubicweb-*
 include man/cubicweb-ctl.1
 
-recursive-include doc *.txt *.zargo *.png *.html makefile
+recursive-include doc *.txt *.zargo *.png *.html makefile *.rst
 
 recursive-include misc *
 
--- a/__init__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/__init__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,7 +2,7 @@
 relations between entitites.
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 :license: Library General Public License version 2 - http://www.gnu.org/licenses
 """
@@ -46,7 +46,7 @@
 
 class Binary(StringIO):
     """customize StringIO to make sure we don't use unicode"""
-    def __init__(self, buf= ''):
+    def __init__(self, buf=''):
         assert isinstance(buf, (str, buffer)), \
                "Binary objects must use raw strings, not %s" % buf.__class__
         StringIO.__init__(self, buf)
--- a/__pkginfo__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/__pkginfo__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,12 +1,13 @@
 # pylint: disable-msg=W0622,C0103
 """cubicweb global packaging information for the cubicweb knowledge management
 software
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 distname = "cubicweb"
 modname = "cubicweb"
 
-numversion = (3, 2, 0)
+numversion = (3, 2, 3)
 version = '.'.join(str(num) for num in numversion)
 
 license = 'LGPL v2'
--- a/_exceptions.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/_exceptions.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -26,9 +27,9 @@
     """a misconfiguration error"""
 
 class InternalError(CubicWebException):
-    """base class for exceptions which should not occurs"""    
+    """base class for exceptions which should not occurs"""
 
-class SecurityError(CubicWebException): 
+class SecurityError(CubicWebException):
     """base class for cubicweb server security exception"""
 
 class RepositoryError(CubicWebException):
@@ -39,7 +40,7 @@
 
 class CubicWebRuntimeError(CubicWebException):
     """base class for runtime exceptions"""
-    
+
 # repository exceptions #######################################################
 
 class ConnectionError(RepositoryError):
@@ -53,7 +54,7 @@
 class BadConnectionId(ConnectionError):
     """raised when a bad connection id is given or when an attempt to establish
     a connection failed"""
-    
+
 BadSessionId = BadConnectionId # XXX bw compat for pyro connections
 
 class UnknownEid(RepositoryError):
@@ -68,7 +69,7 @@
     """no source support a relation type"""
     msg = 'No source supports %r relation\'s type'
 
-    
+
 # security exceptions #########################################################
 
 class Unauthorized(SecurityError):
@@ -80,7 +81,7 @@
     var = None
     #def __init__(self, *args):
     #    self.args = args
-        
+
     def __str__(self):
         try:
             if self.args and len(self.args) == 2:
@@ -90,7 +91,7 @@
             return self.msg
         except Exception, ex:
             return str(ex)
-    
+
 # source exceptions ###########################################################
 
 class EidNotInSource(SourceException):
@@ -98,8 +99,8 @@
     source has failed
     """
     msg = 'No entity with eid %s in %s'
-    
-    
+
+
 # registry exceptions #########################################################
 
 class RegistryException(CubicWebException):
@@ -110,16 +111,16 @@
 
     this is usually a programming/typo error...
     """
-    
+
 class ObjectNotFound(RegistryException):
     """raised when an unregistered object is requested
 
     this may be a programming/typo or a misconfiguration error
     """
-    
+
 # class ViewNotFound(ObjectNotFound):
 #     """raised when an unregistered view is called"""
-    
+
 class NoSelectableObject(RegistryException):
     """some views with the given vid have been found but no
     one is applyable to the result set
@@ -144,5 +145,5 @@
     """server execution control error (already started, not running...)"""
 
 # pylint: disable-msg=W0611
-from logilab.common.clcommands import BadCommandUsage 
+from logilab.common.clcommands import BadCommandUsage
 
--- a/appobject.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/appobject.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,12 +1,13 @@
 """Base class for dynamically loaded objects manipulated in the web interface
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
-from datetime import datetime, timedelta
+from datetime import datetime, timedelta, time
 
 from logilab.common.decorators import classproperty
 from logilab.common.deprecation import obsolete
@@ -17,7 +18,7 @@
 from cubicweb import Unauthorized, NoSelectableObject
 from cubicweb.vregistry import VObject, AndSelector
 from cubicweb.selectors import yes
-from cubicweb.utils import UStringIO, ustrftime
+from cubicweb.utils import UStringIO, ustrftime, strptime, todate, todatetime
 
 ONESECOND = timedelta(0, 1, 0)
 
@@ -187,14 +188,8 @@
         return rql
 
     def view(self, __vid, rset=None, __fallback_vid=None, **kwargs):
-        """shortcut to self.vreg.render method avoiding to pass self.req"""
-        try:
-            view = self.vreg.select_view(__vid, self.req, rset, **kwargs)
-        except NoSelectableObject:
-            if __fallback_vid is None:
-                raise
-            view = self.vreg.select_view(__fallback_vid, self.req, rset, **kwargs)
-        return view.render(**kwargs)
+        """shortcut to self.vreg.view method avoiding to pass self.req"""
+        return self.vreg.view(__vid, self.req, rset, __fallback_vid, **kwargs)
 
     def initialize_varmaker(self):
         varmaker = self.req.get_page_data('rql_varmaker')
@@ -300,6 +295,35 @@
             return self.req.property_value('ui.float-format') % num
         return u''
 
+    def parse_datetime(self, value, etype='Datetime'):
+        """get a datetime or time from a string (according to etype)
+        Datetime formatted as Date are accepted
+        """
+        assert etype in ('Datetime', 'Date', 'Time'), etype
+        # XXX raise proper validation error
+        if etype == 'Datetime':
+            format = self.req.property_value('ui.datetime-format')
+            try:
+                return todatetime(strptime(value, format))
+            except ValueError:
+                pass
+        elif etype == 'Time':
+            format = self.req.property_value('ui.time-format')
+            try:
+                # (adim) I can't find a way to parse a Time with a custom format
+                date = strptime(value, format) # this returns a DateTime
+                return time(date.hour, date.minute, date.second)
+            except ValueError:
+                raise ValueError('can\'t parse %r (expected %s)' % (value, format))
+        try:
+            format = self.req.property_value('ui.date-format')
+            dt = strptime(value, format)
+            if etype == 'Datetime':
+                return todatetime(dt)
+            return todate(dt)
+        except ValueError:
+            raise ValueError('can\'t parse %r (expected %s)' % (value, format))
+
     # security related methods ################################################
 
     def ensure_ro_rql(self, rql):
--- a/common/__init__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/__init__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 hg stserver side and on the client side
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 from logilab.common.adbh import FunctionDescr
@@ -15,38 +16,38 @@
 class COMMA_JOIN(FunctionDescr):
     supported_backends = ('postgres', 'sqlite',)
     rtype = 'String'
-    
+
     @classmethod
     def st_description(cls, funcnode):
         return ', '.join(term.get_description()
                          for term in iter_funcnode_variables(funcnode))
-    
+
 register_function(COMMA_JOIN)  # XXX do not expose?
 
 
 class CONCAT_STRINGS(COMMA_JOIN):
     aggregat = True
-    
+
 register_function(CONCAT_STRINGS) # XXX bw compat
 
 class GROUP_CONCAT(CONCAT_STRINGS):
     supported_backends = ('mysql', 'postgres', 'sqlite',)
-    
+
 register_function(GROUP_CONCAT)
 
 
 class LIMIT_SIZE(FunctionDescr):
     supported_backends = ('postgres', 'sqlite',)
     rtype = 'String'
-    
+
     @classmethod
     def st_description(cls, funcnode):
         return funcnode.children[0].get_description()
-    
+
 register_function(LIMIT_SIZE)
 
 
 class TEXT_LIMIT_SIZE(LIMIT_SIZE):
     supported_backends = ('mysql', 'postgres', 'sqlite',)
-    
+
 register_function(TEXT_LIMIT_SIZE)
--- a/common/appobject.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/appobject.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""pre 3.2 bw compat"""
+"""pre 3.2 bw compat
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 # pylint: disable-msg=W0614,W0401
 from warnings import warn
 warn('moved to cubicweb.appobject', DeprecationWarning, stacklevel=2)
--- a/common/entity.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/entity.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""pre 3.2 bw compat"""
+"""pre 3.2 bw compat
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 # pylint: disable-msg=W0614,W0401
 from warnings import warn
 warn('moved to cubicweb.entity', DeprecationWarning, stacklevel=2)
--- a/common/i18n.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/i18n.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Some i18n/gettext utilities.
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -90,4 +91,3 @@
         except Exception:
             continue
     return errors
-                         
--- a/common/mail.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/mail.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Common utilies to format / semd emails.
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/common/migration.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/migration.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 version
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -125,10 +126,10 @@
 
     def repo_connect(self):
         return self.config.repository()
-        
+
     def migrate(self, vcconf, toupgrade, options):
         """upgrade the given set of cubes
-        
+
         `cubes` is an ordered list of 3-uple:
         (cube, fromversion, toversion)
         """
@@ -149,23 +150,23 @@
                                    'versions_map': vmap})
             self.scripts_session(scripts)
         else:
-            print 'no migration script to execute'            
+            print 'no migration script to execute'
 
     def shutdown(self):
         pass
-    
+
     def __getattribute__(self, name):
         try:
             return object.__getattribute__(self, name)
         except AttributeError:
             cmd = 'cmd_%s' % name
             if hasattr(self, cmd):
-                meth = getattr(self, cmd) 
+                meth = getattr(self, cmd)
                 return lambda *args, **kwargs: self.interact(args, kwargs,
                                                              meth=meth)
             raise
         raise AttributeError(name)
-            
+
     def interact(self, args, kwargs, meth):
         """execute the given method according to user's confirmation"""
         msg = 'execute command: %s(%s) ?' % (
@@ -224,7 +225,7 @@
         except ImportError:
             # readline not available
             pass
-        else:        
+        else:
             readline.set_completer(Completer(local_ctx).complete)
             readline.parse_and_bind('tab: complete')
             histfile = os.path.join(os.environ["HOME"], ".eshellhist")
@@ -256,7 +257,7 @@
                 else:
                     context[attr[4:]] = getattr(self, attr)
         return context
-    
+
     def process_script(self, migrscript, funcname=None, *args, **kwargs):
         """execute a migration script
         in interactive mode,  display the migration script path, ask for
@@ -280,7 +281,7 @@
                     self.critical('no %s in script %s', funcname, migrscript)
                     return None
                 return func(*args, **kwargs)
-                    
+
     def scripts_session(self, migrscripts):
         """execute some scripts in a transaction"""
         try:
@@ -311,7 +312,7 @@
     def cmd_option_type_changed(self, optname, oldtype, newvalue):
         """a configuration option's type has changed"""
         self._option_changes.append(('typechanged', optname, oldtype, newvalue))
-        
+
     def cmd_add_cubes(self, cubes):
         """modify the list of used cubes in the in-memory config
         returns newly inserted cubes, including dependencies
@@ -319,7 +320,7 @@
         if isinstance(cubes, basestring):
             cubes = (cubes,)
         origcubes = self.config.cubes()
-        newcubes = [p for p in self.config.expand_cubes(cubes) 
+        newcubes = [p for p in self.config.expand_cubes(cubes)
                        if not p in origcubes]
         if newcubes:
             for cube in cubes:
@@ -340,7 +341,7 @@
         assert cube in removed, \
                "can't remove cube %s, used as a dependancy" % cube
         return removed
-    
+
     def rewrite_configuration(self):
         # import locally, show_diffs unavailable in gae environment
         from cubicweb.toolsutils import show_diffs
--- a/common/mixins.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/mixins.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -236,8 +237,8 @@
 
     @obsolete('use EntityFieldsForm.subject_in_state_vocabulary')
     def subject_in_state_vocabulary(self, rschema, limit=None):
-        from cubicweb.web.form import EntityFieldsForm
-        return EntityFieldsForm(self.req, None, entity=self).subject_in_state_vocabulary(rschema, limit)
+        form = self.vreg.select_object('forms', 'edition', self.req, entity=self)
+        return form.subject_in_state_vocabulary(rschema, limit)
 
 
 
--- a/common/mttransforms.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/mttransforms.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """mime type transformation engine for cubicweb, based on mtconverter
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/common/schema.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/schema.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""pre 3.0 bw compat"""
+"""pre 3.0 bw compat
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 # pylint: disable-msg=W0614,W0401
 from warnings import warn
 warn('moved to cubicweb.schema', DeprecationWarning, stacklevel=2)
--- a/common/selectors.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/selectors.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""pre 3.2 bw compat"""
+"""pre 3.2 bw compat
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 # pylint: disable-msg=W0614,W0401
 from warnings import warn
 warn('moved to cubicweb.selectors', DeprecationWarning, stacklevel=2)
--- a/common/tags.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/tags.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """helper classes to generate simple (X)HTML tags
 
 :organization: Logilab
-:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/common/test/data/migration/0.0.3_Any.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/test/data/migration/0.0.3_Any.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,8 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 coucou
--- a/common/test/data/migration/0.0.4_Any.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/test/data/migration/0.0.4_Any.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,8 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 coucou
--- a/common/test/data/migration/0.1.0_Any.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/test/data/migration/0.1.0_Any.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,8 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 coucou
--- a/common/test/data/migration/0.1.0_common.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/test/data/migration/0.1.0_common.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,7 @@
-"""common to all configuration"""
+"""common to all configuration
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
--- a/common/test/data/migration/0.1.0_repository.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/test/data/migration/0.1.0_repository.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,7 @@
-"""repository specific"""
+"""repository specific
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
--- a/common/test/data/migration/0.1.0_web.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/test/data/migration/0.1.0_web.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,7 @@
-"""web only"""
+"""web only
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
--- a/common/test/data/migration/0.1.2_Any.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/test/data/migration/0.1.2_Any.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,8 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 coucou
--- a/common/test/data/server_migration/bootstrapmigration_repository.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/test/data/server_migration/bootstrapmigration_repository.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,7 @@
-"""allways executed before all others in server migration"""
+"""allways executed before all others in server migration
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
--- a/common/test/unittest_mail.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/test/unittest_mail.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,5 +1,11 @@
 # -*- coding: utf-8 -*-
-"""unit tests for module cubicweb.common.mail"""
+"""unit tests for module cubicweb.common.mail
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 import os
 import pwd
@@ -17,14 +23,14 @@
     Another solution would be to use $LOGNAME, $USER or $USERNAME
     """
     return pwd.getpwuid(os.getuid())[0]
-    
+
 
 class EmailTC(EnvBasedTC):
 
     def test_format_mail(self):
         self.set_option('sender-addr', 'bim@boum.fr')
         self.set_option('sender-name', 'BimBam')
-        
+
         mail = format_mail({'name': 'oim', 'email': 'oim@logilab.fr'},
                            ['test@logilab.fr'], u'un petit cöucou', u'bïjour',
                            config=self.config)
@@ -47,7 +53,7 @@
         self.assertEquals(msg.get('reply-to'), u'oim <oim@logilab.fr>, BimBam <bim@boum.fr>')
         self.assertEquals(msg.get_payload(decode=True), u'un petit cöucou')
 
-        
+
     def test_format_mail_euro(self):
         mail = format_mail({'name': u'oîm', 'email': u'oim@logilab.fr'},
                            ['test@logilab.fr'], u'un petit cöucou €', u'bïjour €')
@@ -92,7 +98,7 @@
         self.assertEquals(msg.get('reply-to'), u'tutu <tutu@logilab.fr>')
         # set sender name and address as expected
         self.set_option('sender-name', 'cubicweb-test')
-        self.set_option('sender-addr', 'cubicweb-test@logilab.fr') 
+        self.set_option('sender-addr', 'cubicweb-test@logilab.fr')
         # anonymous notification: no name and no email specified
         msg = format_mail({'name': u'', 'email': u''},
                            ['test@logilab.fr'], u'un petit cöucou €', u'bïjour €',
@@ -119,4 +125,4 @@
 
 if __name__ == '__main__':
     unittest_main()
-    
+
--- a/common/test/unittest_migration.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/test/unittest_migration.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""cubicweb.common.migration unit tests"""
+"""cubicweb.common.migration unit tests
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 from os.path import abspath
 from logilab.common.testlib import TestCase, unittest_main
@@ -24,7 +30,7 @@
 
     def cube_migration_scripts_dir(cls, cube):
         return TMIGRDIR
-    
+
 class MigrationToolsTC(TestCase):
     def setUp(self):
         self.config = MigrTestConfig('data')
@@ -32,7 +38,7 @@
         self.config.load_schema = lambda expand_cubes=False: Schema('test')
         self.config.__class__.cubicweb_vobject_path = frozenset()
         self.config.__class__.cube_vobject_path = frozenset()
-        
+
     def test_migration_files_base(self):
         self.assertListEquals(migration_files(self.config, [('cubicweb', (2,3,0), (2,4,0)),
                                                             ('TEMPLATE', (0,0,2), (0,0,3))]),
@@ -48,7 +54,7 @@
                               [SMIGRDIR+'bootstrapmigration_repository.py',
                                SMIGRDIR+'2.6.0_Any.sql',
                                TMIGRDIR+'0.0.4_Any.py'])
-        
+
 ##     def test_migration_files_overlap(self):
 ##         self.assertListEquals(migration_files(self.config, (2,4,0), (2,10,2),
 ##                                               (0,0,2), (0,1,2)),
@@ -62,7 +68,7 @@
 ##                                TMIGRDIR+'0.1.0_repository.py',
 ##                                TMIGRDIR+'0.1.2_Any.py',
 ##                                SMIGRDIR+'2.10.1_2.10.2_Any.sql'])
-        
+
     def test_migration_files_for_mode(self):
         from cubicweb.server.migractions import ServerMigrationHelper
         self.assertIsInstance(self.config.migration_handler(), ServerMigrationHelper)
--- a/common/test/unittest_mixins.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/test/unittest_mixins.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from logilab.common.testlib import unittest_main
 from cubicweb.devtools.apptest import EnvBasedTC
 
@@ -8,7 +15,7 @@
                      {'x': s.eid})
         es = self.user().wf_state('activated')
         self.assertEquals(es.state_of[0].name, 'CWUser')
-        
+
     def test_wf_transition(self):
         t = self.add_entity('Transition', name=u'deactivate')
         self.execute('SET X transition_of ET WHERE ET name "Bookmark", X eid %(x)s',
@@ -20,6 +27,6 @@
         user = self.user()
         user.change_state(user.wf_state('deactivated').eid)
         self.assertEquals(user.state, 'deactivated')
-    
+
 if __name__ == '__main__':
     unittest_main()
--- a/common/test/unittest_uilib.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/test/unittest_uilib.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,5 +1,11 @@
 # -*- coding: utf-8 -*-
-"""unittests for cubicweb.common.uilib"""
+"""unittests for cubicweb.common.uilib
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 __docformat__ = "restructuredtext en"
 
--- a/common/uilib.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/uilib.py	Wed Jun 03 09:15:20 2009 +0200
@@ -4,8 +4,9 @@
 contains some functions designed to help implementation of cubicweb user interface
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/common/utils.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/utils.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""pre 3.2 bw compat"""
+"""pre 3.2 bw compat
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 # pylint: disable-msg=W0614,W0401
 from warnings import warn
 warn('moved to cubicweb.utils', DeprecationWarning, stacklevel=2)
--- a/common/view.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/common/view.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""pre 3.2 bw compat"""
+"""pre 3.2 bw compat
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 # pylint: disable-msg=W0614,W0401
 from warnings import warn
 warn('moved to cubicweb.view', DeprecationWarning, stacklevel=2)
--- a/cwconfig.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/cwconfig.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,15 +2,17 @@
 """common configuration utilities for cubicweb
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 
 .. envvar:: CW_CUBES_PATH
 
    Augments the default search path for cubes
-   
+
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
+_ = unicode
 
 import sys
 import os
@@ -27,7 +29,6 @@
 
 CONFIGURATIONS = []
 
-_ = unicode
 
 class metaconfiguration(type):
     """metaclass to automaticaly register configuration"""
--- a/cwctl.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/cwctl.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,6 +1,7 @@
 """%%prog %s [options] %s
 
 CubicWeb main applications controller.
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 %s"""
 
 import sys
@@ -744,10 +745,10 @@
       identifiers of the applications to consider. If no application is
       given, recompile for all registered applications.
     """
-    name = 'i18ncompile'
+    name = 'i18ninstance'
 
     @staticmethod
-    def i18ncompile_application(appid):
+    def i18ninstance_application(appid):
         """recompile application's messages catalogs"""
         config = cwcfg.config_for(appid)
         try:
--- a/cwvreg.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/cwvreg.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """extend the generic VRegistry with some cubicweb specific stuff
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 _ = unicode
@@ -11,7 +12,7 @@
 
 from rql import RQLHelper
 
-from cubicweb import Binary, UnknownProperty, UnknownEid
+from cubicweb import ETYPE_NAME_MAP, Binary, UnknownProperty, UnknownEid
 from cubicweb.vregistry import VRegistry, ObjectNotFound, NoSelectableObject
 from cubicweb.rtags import RTAGS
 
@@ -170,7 +171,10 @@
         # browse ancestors from most specific to most generic and
         # try to find an associated custom entity class
         for baseschema in baseschemas:
-            btype = str(baseschema)
+            try:
+                btype = ETYPE_NAME_MAP[baseschema]
+            except KeyError:
+                btype = str(baseschema)
             try:
                 cls = self.select(self.registry_objects('etypes', btype), etype)
                 break
@@ -245,6 +249,16 @@
                 self.exception('error while trying to list possible %s views for %s',
                                vid, rset)
 
+    def view(self, __vid, req, rset=None, __fallback_vid=None, **kwargs):
+        """shortcut to self.vreg.render method avoiding to pass self.req"""
+        try:
+            view = self.select_view(__vid, req, rset, **kwargs)
+        except NoSelectableObject:
+            if __fallback_vid is None:
+                raise
+            view = self.select_view(__fallback_vid, req, rset, **kwargs)
+        return view.render(**kwargs)
+
     def select_box(self, oid, *args, **kwargs):
         """return the most specific view according to the result set"""
         try:
@@ -266,12 +280,11 @@
         except (NoSelectableObject, ObjectNotFound):
             return
 
-    def select_view(self, __vid, req, rset, **kwargs):
+    def select_view(self, __vid, req, rset=None, **kwargs):
         """return the most specific view according to the result set"""
         views = self.registry_objects('views', __vid)
         return self.select(views, req, rset, **kwargs)
 
-
     # properties handling #####################################################
 
     def user_property_keys(self, withsitewide=False):
@@ -372,6 +385,8 @@
         default to a dump of the class registered for 'Any'
         """
         usercls = super(MulCnxCubicWebRegistry, self).etype_class(etype)
+        if etype == 'Any':
+            return usercls
         usercls.e_schema = self.schema.eschema(etype)
         return usercls
 
@@ -385,7 +400,14 @@
             vobject.vreg = self
             vobject.schema = self.schema
             vobject.config = self.config
-        return super(MulCnxCubicWebRegistry, self).select(vobjects, *args, **kwargs)
+        selected = super(MulCnxCubicWebRegistry, self).select(vobjects, *args,
+                                                              **kwargs)
+        # redo the same thing on the instance so it won't use equivalent class
+        # attributes (which may change)
+        selected.vreg = self
+        selected.schema = self.schema
+        selected.config = self.config
+        return selected
 
 from datetime import datetime, date, time, timedelta
 
--- a/dbapi.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/dbapi.py	Wed Jun 03 09:15:20 2009 +0200
@@ -5,15 +5,17 @@
 (most parts of this document are reported here in docstrings)
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
 from logging import getLogger
 from time import time, clock
 
-from cubicweb import ConnectionError, RequestSessionMixIn, set_log_methods
+from logilab.common.logging_ext import set_log_methods
+from cubicweb import ETYPE_NAME_MAP, ConnectionError, RequestSessionMixIn
 from cubicweb.cwvreg import CubicWebRegistry, MulCnxCubicWebRegistry
 from cubicweb.cwconfig import CubicWebNoAppConfiguration
 
@@ -50,7 +52,7 @@
         # resolve the Pyro object
         try:
             nshost, nsport = config['pyro-ns-host'], config['pyro-ns-port']
-            uri = locator.getNS(nshost, nsport) .resolve(nsid)
+            uri = locator.getNS(nshost, nsport).resolve(nsid)
         except ProtocolError:
             raise ConnectionError('Could not connect to the Pyro name server '
                                   '(host: %s:%i)' % (nshost, nsport))
@@ -101,7 +103,12 @@
             vreg = MulCnxCubicWebRegistry(config, initlog=initlog)
         else:
             vreg = CubicWebRegistry(config, initlog=initlog)
-        vreg.set_schema(repo.get_schema())
+        schema = repo.get_schema()
+        for oldetype, newetype in ETYPE_NAME_MAP.items():
+            if oldetype in schema:
+                print 'aliasing', newetype, 'to', oldetype
+                schema._entities[newetype] = schema._entities[oldetype]
+        vreg.set_schema(schema)
     else:
         vreg = None
     cnx = repo_connect(repo, user, password, cnxprops)
--- a/debian/changelog	Wed Jun 03 09:09:33 2009 +0200
+++ b/debian/changelog	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,21 @@
+cubicweb (3.2.3-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- Nicolas Chauvat <nicolas.chauvat@logilab.fr>  Wed, 27 May 2009 12:31:49 +0200
+
+cubicweb (3.2.1-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- Aurélien Campéas <aurelien.campeas@logilab.fr>  Mon, 25 May 2009 16:45:00 +0200
+
+cubicweb (3.2.0-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr>  Thu, 14 May 2009 12:31:06 +0200
+
 cubicweb (3.1.4-1) unstable; urgency=low
 
   * new upstream release
--- a/debian/control	Wed Jun 03 09:09:33 2009 +0200
+++ b/debian/control	Wed Jun 03 09:15:20 2009 +0200
@@ -75,7 +75,7 @@
 Package: cubicweb-common
 Architecture: all
 XB-Python-Version: ${python:Versions}
-Depends: ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.6.0), python-logilab-common (>= 0.39.0), python-yams (>= 0.22.0), python-rql (>= 0.22.0)
+Depends: ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.6.0), python-logilab-common (>= 0.41.0), python-yams (>= 0.23.0), python-rql (>= 0.22.0)
 Recommends: python-simpletal (>= 4.0), python-lxml
 Conflicts: cubicweb-core
 Replaces: cubicweb-core
--- a/debian/cubicweb-common.install.in	Wed Jun 03 09:09:33 2009 +0200
+++ b/debian/cubicweb-common.install.in	Wed Jun 03 09:15:20 2009 +0200
@@ -1,17 +1,5 @@
 debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/common/ usr/lib/PY_VERSION/site-packages/cubicweb
 debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/entities/ usr/lib/PY_VERSION/site-packages/cubicweb
+debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/ext/ usr/lib/PY_VERSION/site-packages/cubicweb
 debian/tmp/usr/share/cubicweb/cubes/shared/i18n usr/share/cubicweb/cubes/shared/
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/rset.py usr/share/pyshared/cubicweb
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/gettext.py usr/share/pyshared/cubicweb
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/toolsutils.py usr/share/pyshared/cubicweb
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/cwvreg.py usr/share/pyshared/cubicweb
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/_exceptions.py usr/share/pyshared/cubicweb
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/schemaviewer.py usr/share/pyshared/cubicweb
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/dbapi.py usr/share/pyshared/cubicweb
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/cwconfig.py usr/share/pyshared/cubicweb
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/__init__.py usr/share/pyshared/cubicweb
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/md5crypt.py usr/share/pyshared/cubicweb
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/schema.py usr/share/pyshared/cubicweb
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/interfaces.py usr/share/pyshared/cubicweb
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/vregistry.py usr/share/pyshared/cubicweb
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/__pkginfo__.py usr/share/pyshared/cubicweb
+debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/*.py usr/share/pyshared/cubicweb
--- a/debian/cubicweb-ctl.bash_completion	Wed Jun 03 09:09:33 2009 +0200
+++ b/debian/cubicweb-ctl.bash_completion	Wed Jun 03 09:15:20 2009 +0200
@@ -75,7 +75,7 @@
 	    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$options $instances' -- "$cur"))
 	;;
 	# commands with template as argument
- 	i18nupdate)
+ 	i18ncube)
 	    cubes="$("$ec" listcubes 2>/dev/null)" || cubes=""
 	    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$options $cubes' -- "$cur"))
  	;;
@@ -86,12 +86,12 @@
 	    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$options $instances' -- "$cur"))
  	;;
 	# generic commands without argument
- 	list|newtemplate|i18nlibupdate|live-server)
+ 	list|newtemplate|i18ncubicweb|live-server)
 	    options="$("$ec" listcommands "$cmd" 2>/dev/null)" || options=""
 	    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$options $instances' -- "$cur"))
  	;;
 	# generic commands without option
- 	shell|i18ncompile|delete|status|schema-sync)
+ 	shell|i18ninstance|delete|status|schema-sync)
 	    instances="$("$ec" listinstances 2>/dev/null)" || instances=""
 	    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$options $instances' -- "$cur"))
  	;;
--- a/debian/cubicweb-dev.install.in	Wed Jun 03 09:09:33 2009 +0200
+++ b/debian/cubicweb-dev.install.in	Wed Jun 03 09:15:20 2009 +0200
@@ -1,7 +1,2 @@
 debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/devtools/ usr/lib/PY_VERSION/site-packages/cubicweb/
 debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/skeleton/ usr/lib/PY_VERSION/site-packages/cubicweb/
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/test usr/lib/PY_VERSION/site-packages/cubicweb/
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/common/test usr/lib/PY_VERSION/site-packages/cubicweb/common/
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/server/test usr/lib/PY_VERSION/site-packages/cubicweb/server/
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/sobjects/test usr/lib/PY_VERSION/site-packages/cubicweb/sobjects/
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/web/test usr/lib/PY_VERSION/site-packages/cubicweb/web/
--- a/debian/rules	Wed Jun 03 09:09:33 2009 +0200
+++ b/debian/rules	Wed Jun 03 09:15:20 2009 +0200
@@ -39,13 +39,20 @@
 	# Put all the python library and data in cubicweb-common
 	# and scripts in cubicweb-server
 	dh_install -vi
+	# cwctl in the cubicweb-ctl package
+	rm -f debian/cubicweb-common/usr/share/pyshared/cubicweb/cwctl.py
+	# hercule in the cubicweb-client package
+	rm -f debian/cubicweb-common/usr/share/pyshared/cubicweb/hercule.py
+
 	dh_lintian
 
 	# Remove unittests directory (should be available in cubicweb-dev only)
-	rm -rf debian/cubicweb-server/usr/lib/${PY_VERSION}/site-packages/cubicweb/server/test
-	rm -rf debian/cubicweb-server/usr/lib/${PY_VERSION}/site-packages/cubicweb/sobjects/test
-	rm -rf debian/cubicweb-web/usr/lib/${PY_VERSION}/site-packages/cubicweb/web/test
-	rm -rf debian/cubicweb-common/usr/lib/${PY_VERSION}/site-packages/cubicweb/common/test
+	rm -rf debian/cubicweb-server/usr/share/pyshared/cubicweb/server/test
+	rm -rf debian/cubicweb-server/usr/share/pyshared/cubicweb/sobjects/test
+	rm -rf debian/cubicweb-dev/usr/share/pyshared/cubicweb/devtools/test
+	rm -rf debian/cubicweb-web/usr/share/pyshared/cubicweb/web/test
+	rm -rf debian/cubicweb-common/usr/share/pyshared/cubicweb/common/test
+	rm -rf debian/cubicweb-common/usr/share/pyshared/cubicweb/entities/test
 
 	# cubes directory must be managed as a valid python module
 	touch debian/cubicweb-common/usr/share/cubicweb/cubes/__init__.py
--- a/devtools/__init__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/devtools/__init__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Test tools for cubicweb
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -59,10 +60,10 @@
           'group': 'main', 'inputlevel': 1,
           }),
         ))
-                            
+
     if not os.environ.get('APYCOT_ROOT'):
         REGISTRY_DIR = normpath(join(CW_SOFTWARE_ROOT, '../cubes'))
-    
+
     def __init__(self, appid, log_threshold=logging.CRITICAL+10):
         ServerConfiguration.__init__(self, appid)
         self.global_set_option('log-file', None)
@@ -71,7 +72,7 @@
         self.load_cwctl_plugins()
 
     anonymous_user = TwistedConfiguration.anonymous_user.im_func
-        
+
     @property
     def apphome(self):
         if exists(self.appid):
@@ -79,7 +80,7 @@
         # application cube test
         return abspath('..')
     appdatahome = apphome
-    
+
     def main_config_file(self):
         """return application's control configuration file"""
         return join(self.apphome, '%s.conf' % self.name)
@@ -116,7 +117,7 @@
         if not sources:
             sources = DEFAULT_SOURCES
         return sources
-    
+
     def load_defaults(self):
         super(TestServerConfiguration, self).load_defaults()
         # note: don't call global set option here, OptionManager may not yet be initialized
@@ -146,25 +147,25 @@
 
     def available_languages(self, *args):
         return ('en', 'fr', 'de')
-    
+
     def ext_resources_file(self):
         """return application's external resources file"""
         return join(self.apphome, 'data', 'external_resources')
-    
+
     def pyro_enabled(self):
         # but export PYRO_MULTITHREAD=0 or you get problems with sqlite and threads
         return True
 
 
 class ApptestConfiguration(BaseApptestConfiguration):
-    
+
     def __init__(self, appid, log_threshold=logging.CRITICAL, sourcefile=None):
         BaseApptestConfiguration.__init__(self, appid, log_threshold=log_threshold)
         self.init_repository = sourcefile is None
         self.sourcefile = sourcefile
         import re
         self.global_set_option('embed-allowed', re.compile('.*'))
-        
+
 
 class RealDatabaseConfiguration(ApptestConfiguration):
     init_repository = False
@@ -180,7 +181,7 @@
                               'password': u'gingkow',
                               },
                    }
-    
+
     def __init__(self, appid, log_threshold=logging.CRITICAL, sourcefile=None):
         ApptestConfiguration.__init__(self, appid)
         self.init_repository = False
@@ -191,7 +192,7 @@
         By default, we run tests with the sqlite DB backend.
         One may use its own configuration by just creating a
         'sources' file in the test directory from wich tests are
-        launched. 
+        launched.
         """
         self._sources = self.sourcesdef
         return self._sources
@@ -220,11 +221,11 @@
     """
     return type('MyRealDBConfig', (RealDatabaseConfiguration,),
                 {'sourcesdef': read_config(filename)})
-    
+
 
 class LivetestConfiguration(BaseApptestConfiguration):
     init_repository = False
-    
+
     def __init__(self, cube=None, sourcefile=None, pyro_name=None,
                  log_threshold=logging.CRITICAL):
         TestServerConfiguration.__init__(self, cube, log_threshold=log_threshold)
@@ -254,7 +255,7 @@
             return False
 
 CubicWebConfiguration.cls_adjust_sys_path()
-                                                    
+
 def install_sqlite_path(querier):
     """This patch hotfixes the following sqlite bug :
        - http://www.sqlite.org/cvstrac/tktview?tn=1327,33
@@ -328,21 +329,21 @@
         pass
     if removecube:
         try:
-            os.remove('%s-cube' % dbfile)
+            os.remove('%s-template' % dbfile)
         except OSError:
             pass
-    
+
 def init_test_database_sqlite(config, source, vreg=None):
     """initialize a fresh sqlite databse used for testing purpose"""
     import shutil
     # remove database file if it exists (actually I know driver == 'sqlite' :)
     dbfile = source['system']['db-name']
     cleanup_sqlite(dbfile)
-    cube = '%s-cube' % dbfile
-    if exists(cube):
-        shutil.copy(cube, dbfile)
+    template = '%s-template' % dbfile
+    if exists(template):
+        shutil.copy(template, dbfile)
     else:
         # initialize the database
         from cubicweb.server import init_repository
         init_repository(config, interactive=False, vreg=vreg)
-        shutil.copy(dbfile, cube)
+        shutil.copy(dbfile, template)
--- a/devtools/_apptest.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/devtools/_apptest.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Hidden internals for the devtools.apptest module
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -20,7 +21,7 @@
 
 from cubicweb.devtools import ApptestConfiguration, init_test_database
 from cubicweb.devtools.fake import FakeRequest
-    
+
 SYSTEM_ENTITIES = ('CWGroup', 'CWUser',
                    'CWAttribute', 'CWRelation',
                    'CWConstraint', 'CWConstraintType', 'CWProperty',
@@ -35,7 +36,7 @@
     'is', 'is_instance_of', 'owned_by', 'created_by', 'specializes',
     # workflow related
     'state_of', 'transition_of', 'initial_state', 'allowed_transition',
-    'destination_state', 'in_state', 'wf_info_for', 'from_state', 'to_state', 
+    'destination_state', 'in_state', 'wf_info_for', 'from_state', 'to_state',
     'condition',
     # permission
     'in_group', 'require_group', 'require_permission',
@@ -46,7 +47,7 @@
     'relation_type', 'from_entity', 'to_entity',
     'constrained_by', 'cstrtype', 'widget',
     # deducted from other relations
-    'primary_email', 
+    'primary_email',
                     )
 
 def unprotected_entities(app_schema, strict=False):
@@ -58,7 +59,7 @@
         protected_entities = yams.schema.BASE_TYPES.union(set(SYSTEM_ENTITIES))
     entities = set(app_schema.entities())
     return entities - protected_entities
-    
+
 
 def ignore_relations(*relations):
     global SYSTEM_RELATIONS
@@ -68,7 +69,7 @@
     """TestEnvironment defines a context (e.g. a config + a given connection) in
     which the tests are executed
     """
-    
+
     def __init__(self, appid, reporter=None, verbose=False,
                  configcls=ApptestConfiguration, requestcls=FakeRequest):
         config = configcls(appid)
@@ -114,7 +115,7 @@
         self.cnx.vreg = self.vreg
         self.cnx.login = source['db-user']
         self.cnx.password = source['db-password']
-        
+
 
     def create_user(self, login, groups=('users',), req=None):
         req = req or self.create_request()
@@ -140,7 +141,7 @@
         if login == self.vreg.config.anonymous_user()[0]:
             self.cnx.anonymous_connection = True
         return self.cnx
-    
+
     def restore_connection(self):
         if not self.cnx is self._orig_cnx:
             try:
@@ -157,7 +158,7 @@
         """
         req = req or self.create_request(rql=rql)
         return self.cnx.cursor(req).execute(unicode(rql), args, eidkey)
-    
+
     def create_request(self, rql=None, **kwargs):
         """executes <rql>, builds a resultset, and returns a
         couple (rset, req) where req is a FakeRequest
@@ -167,14 +168,14 @@
         req = self.requestcls(self.vreg, form=kwargs)
         req.set_connection(self.cnx)
         return req
-        
+
     def get_rset_and_req(self, rql, optional_args=None, args=None, eidkey=None):
         """executes <rql>, builds a resultset, and returns a
         couple (rset, req) where req is a FakeRequest
         """
         return (self.execute(rql, args, eidkey),
                 self.create_request(rql=rql, **optional_args or {}))
-    
+
     def check_view(self, rql, vid, optional_args, template='main'):
         """checks if vreg.view() raises an exception in this environment
 
@@ -183,7 +184,7 @@
         """
         return self.call_view(vid, rql,
                               template=template, optional_args=optional_args)
-    
+
     def call_view(self, vid, rql, template='main', optional_args=None):
         """shortcut for self.vreg.view()"""
         assert template
@@ -227,7 +228,7 @@
             yield action
 
 class ExistingTestEnvironment(TestEnvironment):
-    
+
     def __init__(self, appid, sourcefile, verbose=False):
         config = ApptestConfiguration(appid, sourcefile=sourcefile)
         if verbose:
@@ -237,12 +238,12 @@
         self.cnx = init_test_database(driver=source['db-driver'],
                                       vreg=self.vreg)[1]
         if verbose:
-            print "init done" 
+            print "init done"
         self.app = CubicWebPublisher(config, vreg=self.vreg)
         self.verbose = verbose
         # this is done when the publisher is opening a connection
         self.cnx.vreg = self.vreg
-        
+
     def setup(self, config=None):
         """config is passed by TestSuite but is ignored in this environment"""
         cursor = self.cnx.cursor()
@@ -254,4 +255,3 @@
         cursor.execute('DELETE Any X WHERE X eid > %(x)s', {'x' : self.last_eid}, eid_key='x')
         print "cleaning done"
         self.cnx.commit()
-
--- a/devtools/apptest.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/devtools/apptest.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """This module provides misc utilities to test applications
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/devtools/cwtwill.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/devtools/cwtwill.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""cubicweb extensions for twill"""
+"""cubicweb extensions for twill
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 import re
 from urllib import quote
@@ -24,9 +30,9 @@
                 # if url is specified linkurl must match
                 if url and linkurl != url:
                     continue
-                return        
+                return
     raise AssertionError('link %s (%s) not found' % (text, url))
-        
+
 
 def view(rql, vid=''):
     """
@@ -56,7 +62,7 @@
     twc.go('view?rql=%s&vid=edition' % quote(rql))
 
 
-        
+
 
 def setvalue(formname, fieldname, value):
     """
@@ -104,5 +110,5 @@
     browser._browser.form = form
     browser.submit(submit_button)
 
-    
+
 # missing actions: delete, copy, changeview
--- a/devtools/devctl.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/devtools/devctl.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 cubes development
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -77,11 +78,11 @@
             if mod.__file__.startswith(path):
                 del sys.modules[name]
                 break
-        # fresh rtags
-        from cubicweb import rtags
-        from cubicweb.web import uicfg
-        rtags.RTAGS[:] = []
-        reload(uicfg)
+    # fresh rtags
+    from cubicweb import rtags
+    from cubicweb.web import uicfg
+    rtags.RTAGS[:] = []
+    reload(uicfg)
 
 def generate_schema_pot(w, cubedir=None):
     """generate a pot file with schema specific i18n messages
@@ -246,7 +247,7 @@
     It will regenerate cubicweb/i18n/xx.po files. You'll have then to edit those
     files to add translations of newly added messages.
     """
-    name = 'i18nlibupdate'
+    name = 'i18ncubicweb'
 
     def run(self, args):
         """run the command with its specific arguments"""
@@ -307,14 +308,14 @@
         print 'you can now edit the following files:'
         print '* ' + '\n* '.join(toedit)
         print
-        print "then you'll have to update cubes catalogs using the i18nupdate command"
+        print "then you'll have to update cubes catalogs using the i18ncube command"
 
 
 class UpdateTemplateCatalogCommand(Command):
     """Update i18n catalogs for cubes. If no cube is specified, update
     catalogs of all registered cubes.
     """
-    name = 'i18nupdate'
+    name = 'i18ncube'
     arguments = '[<cube>...]'
 
     def run(self, args):
--- a/devtools/fake.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/devtools/fake.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Fake objects to ease testing of cubicweb without a fully working environment
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/devtools/fill.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/devtools/fill.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 """This modules defines func / methods for creating test repositories
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -33,7 +34,7 @@
         if isinstance(cst, StaticVocabularyConstraint):
             return cst.vocabulary()
     return None
-    
+
 
 def get_max_length(eschema, attrname):
     """returns the maximum length allowed for 'attrname'"""
@@ -75,7 +76,7 @@
             value = self.__generate_value(attrname, index, **kwargs)
         _GENERATED_VALUES.setdefault((self.e_schema.type, attrname), set()).add(value)
         return value
-        
+
     def __generate_value(self, attrname, index, **kwargs):
         """generates a consistent value for 'attrname'"""
         attrtype = str(self.e_schema.destination(attrname)).lower()
@@ -100,7 +101,7 @@
         if choices is None:
             return None
         return unicode(choice(choices)) # FIXME
-        
+
     def generate_string(self, attrname, index, format=None):
         """generates a consistent value for 'attrname' if it's a string"""
         # First try to get choices
@@ -133,7 +134,7 @@
     def generate_password(self, attrname, index):
         """generates a consistent value for 'attrname' if it's a password"""
         return u'toto'
-        
+
     def generate_integer(self, attrname, index):
         """generates a consistent value for 'attrname' if it's an integer"""
         choosed = self.generate_choice(attrname, index)
@@ -145,17 +146,17 @@
         else:
             maxvalue = maxvalue or index
         return randint(minvalue or 0, maxvalue)
-    
+
     generate_int = generate_integer
-    
+
     def generate_float(self, attrname, index):
         """generates a consistent value for 'attrname' if it's a float"""
         return float(randint(-index, index))
-    
+
     def generate_decimal(self, attrname, index):
         """generates a consistent value for 'attrname' if it's a float"""
         return Decimal(str(self.generate_float(attrname, index)))
-    
+
     def generate_date(self, attrname, index):
         """generates a random date (format is 'yyyy-mm-dd')"""
         return date(randint(2000, 2004), randint(1, 12), randint(1, 28))
@@ -163,11 +164,11 @@
     def generate_time(self, attrname, index):
         """generates a random time (format is ' HH:MM')"""
         return timedelta(0, 11, index%60) #'11:%02d' % (index % 60)
-    
+
     def generate_datetime(self, attrname, index):
         """generates a random date (format is 'yyyy-mm-dd HH:MM')"""
         return datetime(randint(2000, 2004), randint(1, 12), randint(1, 28), 11, index%60)
-        
+
 
     def generate_bytes(self, attrname, index, format=None):
         # modpython way
@@ -175,7 +176,7 @@
         fakefile.filename = "file_%s" % attrname
         fakefile.value = fakefile.getvalue()
         return fakefile
-    
+
     def generate_boolean(self, attrname, index):
         """generates a consistent value for 'attrname' if it's a boolean"""
         return index % 2 == 0
@@ -185,7 +186,7 @@
         # need this method else stupid values will be set which make mtconverter
         # raise exception
         return u'application/octet-stream'
-    
+
     def generate_Any_content_format(self, index, **kwargs):
         # content_format attribute of EmailPart has no vocabulary constraint, we
         # need this method else stupid values will be set which make mtconverter
@@ -250,7 +251,7 @@
                             args))
             assert not 'eid' in args, args
         else:
-            queries.append(('INSERT %s X' % etype, {}))        
+            queries.append(('INSERT %s X' % etype, {}))
     return queries
 
 
@@ -365,7 +366,7 @@
                     continue
                 subjcard, objcard = rschema.rproperty(subj, obj, 'cardinality')
                 # process mandatory relations first
-                if subjcard in '1+' or objcard in '1+': 
+                if subjcard in '1+' or objcard in '1+':
                     queries += self.make_relation_queries(sedict, oedict,
                                                           rschema, subj, obj)
                 else:
@@ -374,7 +375,7 @@
                 queries += self.make_relation_queries(sedict, oedict, rschema,
                                                       subj, obj)
         return queries
-        
+
     def qargs(self, subjeids, objeids, subjcard, objcard, subjeid, objeid):
         if subjcard in '?1':
             subjeids.remove(subjeid)
@@ -411,7 +412,7 @@
                         subjeids.remove(subjeid)
         if not subjeids:
             check_card_satisfied(objcard, objeids, subj, rschema, obj)
-            return 
+            return
         if not objeids:
             check_card_satisfied(subjcard, subjeids, subj, rschema, obj)
             return
@@ -452,7 +453,7 @@
                     used.add( (subjeid, objeid) )
                     yield q, self.qargs(subjeids, objeids, subjcard, objcard,
                                         subjeid, objeid)
-                    
+
 def check_card_satisfied(card, remaining, subj, rschema, obj):
     if card in '1+' and remaining:
         raise Exception("can't satisfy cardinality %s for relation %s %s %s"
@@ -466,8 +467,8 @@
     while objeid == avoid: # avoid infinite recursion like in X comment X
         objeid = choice(values)
     return objeid
-                    
-                
+
+
 
 # UTILITIES FUNCS ##############################################################
 def make_tel(num_tel):
--- a/devtools/htmlparser.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/devtools/htmlparser.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""defines a validating HTML parser used in web application tests"""
+"""defines a validating HTML parser used in web application tests
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 import re
 
@@ -88,13 +94,13 @@
         self.input_tags = self.find_tag('input')
         self.title_tags = [self.h1_tags, self.h2_tags, self.h3_tags, self.h4_tags]
 
-    def find_tag(self, tag):
+    def find_tag(self, tag, gettext=True):
         """return a list which contains text of all "tag" elements """
         if self.default_ns is None:
             iterstr = ".//%s" % tag
         else:
             iterstr = ".//{%s}%s" % (self.default_ns, tag)
-        if tag in ('a', 'input'):
+        if not gettext or tag in ('a', 'input'):
             return [(elt.text, elt.attrib) for elt in self.etree.iterfind(iterstr)]
         return [u''.join(elt.xpath('.//text()')) for elt in self.etree.iterfind(iterstr)]
 
--- a/devtools/livetest.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/devtools/livetest.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""provide utilies for web (live) unit testing"""
+"""provide utilies for web (live) unit testing
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 import socket
 import logging
@@ -41,9 +47,9 @@
                 return static.File(str(datadir), segments[1:])
         # Otherwise we use this single resource
         return self, ()
-    
-    
-    
+
+
+
 def make_site(cube, options=None):
     from cubicweb.etwist import twconfig # trigger configuration registration
     sourcefile = options.sourcefile
@@ -75,7 +81,7 @@
 def saveconf(templhome, port, user, passwd):
     import pickle
     conffile = file(join(templhome, 'test', 'livetest.conf'), 'w')
-    
+
     pickle.dump((port, user, passwd, get_starturl(port, user, passwd)),
                 conffile)
     conffile.close()
@@ -99,8 +105,8 @@
     from twill import browser as twb
     twc.OUT = new_output
     twb.OUT = new_output
-    
-    
+
+
 class LiveTestCase(TestCase):
 
     sourcefile = None
@@ -118,7 +124,7 @@
 
     def tearDown(self):
         self.teardown_db(self.cnx)
-    
+
 
     def setup_db(self, cnx):
         """override setup_db() to setup your environment"""
@@ -141,5 +147,3 @@
 
 if __name__ == '__main__':
     runserver()
-
-
--- a/devtools/migrtest.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/devtools/migrtest.py	Wed Jun 03 09:15:20 2009 +0200
@@ -27,11 +27,12 @@
 
 The permissions on .pgpass must disallow any access to world or group;
 achieve this by the command chmod 0600 ~/.pgpass. If the permissions
-are less strict than this, the file will be ignored. 
+are less strict than this, the file will be ignored.
 
 :organization: Logilab
-:copyright: 2001-2006 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/devtools/pkginfo.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/devtools/pkginfo.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""distutils / __pkginfo__ helpers for cubicweb applications"""
+"""distutils / __pkginfo__ helpers for cubicweb applications
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 import os
 from os.path import isdir, join
--- a/devtools/repotest.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/devtools/repotest.py	Wed Jun 03 09:15:20 2009 +0200
@@ -3,8 +3,9 @@
 This module contains functions to initialize a new repository.
 
 :organization: Logilab
-:copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -103,7 +104,7 @@
 
 class RQLGeneratorTC(TestCase):
     schema = None # set this in concret test
-    
+
     def setUp(self):
         self.rqlhelper = RQLHelper(self.schema, special_relations={'eid': 'uid',
                                                                    'has_text': 'fti'})
@@ -114,7 +115,7 @@
     def tearDown(self):
         ExecutionPlan._check_permissions = _orig_check_permissions
         rqlannotation._select_principal = _orig_select_principal
-        
+
     def _prepare(self, rql):
         #print '******************** prepare', rql
         union = self.rqlhelper.parse(rql)
@@ -133,7 +134,7 @@
 
 class BaseQuerierTC(TestCase):
     repo = None # set this in concret test
-    
+
     def setUp(self):
         self.o = self.repo.querier
         self.session = self.repo._sessions.values()[0]
@@ -148,7 +149,7 @@
         return self.session.unsafe_execute('Any MAX(X)')[0][0]
     def cleanup(self):
         self.session.unsafe_execute('DELETE Any X WHERE X eid > %s' % self.maxeid)
-        
+
     def tearDown(self):
         undo_monkey_patch()
         self.session.rollback()
@@ -159,7 +160,7 @@
 
     def set_debug(self, debug):
         set_debug(debug)
-        
+
     def _rqlhelper(self):
         rqlhelper = self.o._rqlhelper
         # reset uid_func so it don't try to get type from eids
@@ -175,8 +176,8 @@
         for select in rqlst.children:
             select.solutions.sort()
         return self.o.plan_factory(rqlst, kwargs, self.session)
-        
-    def _prepare(self, rql, kwargs=None):    
+
+    def _prepare(self, rql, kwargs=None):
         plan = self._prepare_plan(rql, kwargs)
         plan.preprocess(plan.rqlst)
         rqlst = plan.rqlst.children[0]
@@ -195,10 +196,10 @@
 
     def execute(self, rql, args=None, eid_key=None, build_descr=True):
         return self.o.execute(self.session, rql, args, eid_key, build_descr)
-    
+
     def commit(self):
         self.session.commit()
-        self.session.set_pool()        
+        self.session.set_pool()
 
 
 class BasePlannerTC(BaseQuerierTC):
@@ -270,7 +271,7 @@
         def merge_input_maps(self, *args):
             pass
         def _choose_term(self, sourceterms):
-            pass    
+            pass
 _orig_merge_input_maps = PartPlanInformation.merge_input_maps
 _orig_choose_term = PartPlanInformation._choose_term
 
@@ -309,4 +310,3 @@
     ExecutionPlan.init_temp_table = _orig_init_temp_table
     PartPlanInformation.merge_input_maps = _orig_merge_input_maps
     PartPlanInformation._choose_term = _orig_choose_term
-
--- a/devtools/stresstester.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/devtools/stresstester.py	Wed Jun 03 09:15:20 2009 +0200
@@ -5,13 +5,13 @@
 OPTIONS:
   -h / --help
      Display this help message and exit.
-     
+
   -u / --user <user>
      Connect as <user> instead of being prompted to give it.
   -p / --password <password>
      Automatically give <password> for authentication instead of being prompted
      to give it.
-     
+
   -n / --nb-times <num>
      Repeat queries <num> times.
   -t / --nb-threads <num>
@@ -21,8 +21,9 @@
   -o / --report-output <filename>
      Write profiler report into <filename> rather than on stdout
 
-Copyright (c) 2003-2006 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+Copyright (c) 2003-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 __revision__ = "$Id: stresstester.py,v 1.3 2006-03-05 14:35:27 syt Exp $"
@@ -49,7 +50,7 @@
         self._times = times
         self._queries = queries
         self._reporter = reporter
-        
+
     def run(self):
         cursor = self._cursor
         times = self._times
@@ -80,7 +81,7 @@
     threads and can write a report that summarizes all profile informations
     """
     profiler_lock = threading.Lock()
-    
+
     def __init__(self, queries):
         self._queries = tuple(queries)
         self._profile_results = [(0., 0)] * len(self._queries)
@@ -111,8 +112,8 @@
         table_layout = Table(3, rheaders = True, children = table_elems)
         TextWriter().format(table_layout, output)
         # output.write('\n'.join(tmp_output))
-        
-        
+
+
 def run(args):
     """run the command line tool"""
     try:
@@ -150,7 +151,7 @@
         user = raw_input('login: ')
     if password is None:
         password = getpass('password: ')
-    from cubicweb.cwconfig import application_configuration 
+    from cubicweb.cwconfig import application_configuration
     config = application_configuration(args[0])
     # get local access to the repository
     print "Creating repo", prof_file
@@ -176,7 +177,7 @@
     else:
         QueryExecutor(repo_cursor, repeat, queries, reporter = reporter).run()
     reporter.dump_report(report_output)
-    
-    
+
+
 if __name__ == '__main__':
     run(sys.argv[1:])
--- a/devtools/test/data/schema/custom.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/devtools/test/data/schema/custom.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,2 +1,9 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 Person = import_erschema('Person')
 Person.add_relation(Date(), 'birthday')
--- a/devtools/test/data/views/bug.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/devtools/test/data/views/bug.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""only for unit tests !"""
+"""only for unit tests !
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 from cubicweb.view import EntityView
 from cubicweb.selectors import implements
--- a/devtools/test/unittest_dbfill.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/devtools/test/unittest_dbfill.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,5 +1,11 @@
 # -*- coding: iso-8859-1 -*-
-"""unit tests for database value generator"""
+"""unit tests for database value generator
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 import os.path as osp
 import re
@@ -31,7 +37,7 @@
             return getattr(self, '_available_%s_%s' % (etype, attrname))(etype, attrname)
         except AttributeError:
             return None
-    
+
     def _available_Person_firstname(self, etype, attrname):
         return [f.strip() for f in file(osp.join(DATADIR, 'firstnames.txt'))]
 
@@ -51,11 +57,11 @@
         year = date.year
         month = date.month
         day = date.day
-        self.failUnless(day in range(1, 29), '%s not in [0;28]' % day) 
+        self.failUnless(day in range(1, 29), '%s not in [0;28]' % day)
         self.failUnless(month in range(1, 13), '%s not in [1;12]' % month)
         self.failUnless(year in range(2000, 2005),
                         '%s not in [2000;2004]' % year)
-        
+
 
     def test_string(self):
         """test string generation"""
@@ -89,7 +95,7 @@
         for index in range(5):
             date_value = self.person_valgen._generate_value('birthday', index)
             self._check_date(date_value)
-        
+
     def test_phone(self):
         """tests make_tel utility"""
         self.assertEquals(make_tel(22030405), '22 03 04 05')
@@ -102,14 +108,14 @@
                           u'yo')
         self.assertEquals(self.person_valgen._generate_value('description', 12),
                           u'yo')
-                          
-        
+
+
 
 class ConstraintInsertionTC(TestCase):
 
     def test_writeme(self):
         self.skip('Test automatic insertion / Schema Constraints')
-    
+
 
 if __name__ == '__main__':
     unittest_main()
--- a/devtools/test/unittest_fill.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/devtools/test/unittest_fill.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""unit tests for cubicweb.devtools.fill module"""
+"""unit tests for cubicweb.devtools.fill module
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 from logilab.common.testlib import TestCase, unittest_main
 
@@ -20,7 +26,7 @@
         for attrname in attrvalues - set(self.attrvalues):
             delattr(_ValueGenerator, attrname)
 
-        
+
     def test_autoextend(self):
         self.failIf('generate_server' in dir(ValueGenerator))
         class MyValueGenerator(ValueGenerator):
--- a/devtools/test/unittest_testlib.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/devtools/test/unittest_testlib.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""unittests for gct.apptest module"""
+"""unittests for gct.apptest module
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 from cStringIO import StringIO
 from unittest import TestSuite
@@ -22,14 +28,14 @@
             def test_error_view(self):
                 self.add_entity('Bug', title=u"bt")
                 self.view('raising', self.execute('Bug B'), template=None)
-            
+
             def test_correct_view(self):
                 self.view('primary', self.execute('CWUser U'), template=None)
-            
+
         tests = [MyWebTest('test_error_view'), MyWebTest('test_correct_view')]
         result = self.runner.run(TestSuite(tests))
         self.assertEquals(result.testsRun, 2)
-        self.assertEquals(len(result.errors), 0)        
+        self.assertEquals(len(result.errors), 0)
         self.assertEquals(len(result.failures), 1)
 
 
@@ -97,13 +103,13 @@
     def test_source1(self):
         """make sure source is stored correctly"""
         self.assertEquals(self.page_info.source, HTML_PAGE2)
-        
+
     def test_source2(self):
         """make sure source is stored correctly - raise exception"""
         parser = htmlparser.DTDValidator()
         self.assertRaises(AssertionError, parser.parse_string, HTML_PAGE_ERROR)
 
-        
+
     def test_has_title_no_level(self):
         """tests h? tags information"""
         self.assertEquals(self.page_info.has_title('Test'), True)
@@ -128,7 +134,7 @@
         self.assertEquals(self.page_info.has_title_regexp('h[23] title', 2), True)
         self.assertEquals(self.page_info.has_title_regexp('h[23] title', 3), True)
         self.assertEquals(self.page_info.has_title_regexp('h[23] title', 4), False)
-    
+
     def test_appears(self):
         """tests PageInfo.appears()"""
         self.assertEquals(self.page_info.appears('CW'), True)
@@ -151,4 +157,3 @@
 
 if __name__ == '__main__':
     unittest_main()
-
--- a/devtools/testlib.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/devtools/testlib.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """this module contains base classes for web tests
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/doc/book/en/admin/gae.rst	Wed Jun 03 09:09:33 2009 +0200
+++ b/doc/book/en/admin/gae.rst	Wed Jun 03 09:15:20 2009 +0200
@@ -119,13 +119,13 @@
 ``myapp/i18n``. To compile the translation files, use the `gettext` tools
 or the ``laxctl`` command ::
 
-  $ python myapp/bin/laxctl i18nupdate 
-  $ python myapp/bin/laxctl i18ncompile 
+  $ python myapp/bin/laxctl i18ncube 
+  $ python myapp/bin/laxctl i18ninstance
 
 Ignore the errors that print "No translation file found for domain
-'cubicweb'". They disappear after the first run of i18ncompile.
+'cubicweb'". They disappear after the first run of i18ninstance.
 
-.. note:: The command  myapp/bin/laxctl i18nupdate needs to be executed
+.. note:: The command  myapp/bin/laxctl i18ncube needs to be executed
    only if your application is using cubes from cubicweb-apps.
    Otherwise, please skip it.
 
@@ -133,7 +133,7 @@
 recommand you to use ``self.req._("msgId")`` in your application code
 to flag new message id to add to the catalog, where ``_`` refers to
 xgettext that is used to collect new strings to translate. 
-While running ``laxctl i18nupdate``, new string will be added to the catalogs.
+While running ``laxctl i18ncube``, new string will be added to the catalogs.
 
 Generating the data directory
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--- a/doc/book/en/annexes/cookbook.rst	Wed Jun 03 09:09:33 2009 +0200
+++ b/doc/book/en/annexes/cookbook.rst	Wed Jun 03 09:15:20 2009 +0200
@@ -62,3 +62,19 @@
         cnx.commit()
         cnx.close()
 
+
+* How to load data from a script?
+
+  The following script aims at loading data within a script assuming pyro-nsd is
+  running and your application is configured with ``pyro-server=yes``, otherwise
+  you would not be able to use dbapi. ::
+
+    from cubicweb import dbapi
+        
+    cnx = dbapi.connection(database='instance-id', user='admin', password='admin')
+    cur = cnx.cursor()
+    for name in ('Personal', 'Professional', 'Computers'):
+        cur.execute('INSERT Blog B: B name %s', name)
+    cnx.commit()
+
+    
--- a/doc/book/en/annexes/cubicweb-ctl.rst	Wed Jun 03 09:09:33 2009 +0200
+++ b/doc/book/en/annexes/cubicweb-ctl.rst	Wed Jun 03 09:15:20 2009 +0200
@@ -73,9 +73,9 @@
 
 Commands to maintain i18n catalogs
 ----------------------------------
-* ``i18nlibupdate``, regenerates messages catalogs of the `CubicWeb` library
-* ``i18nupdate``, regenerates the messages catalogs of a cube
-* ``i18ncompile``, recompiles the messages catalogs of an instance. 
+* ``i18ncubicweb``, regenerates messages catalogs of the `CubicWeb` library
+* ``i18ncube``, regenerates the messages catalogs of a cube
+* ``i18ninstance``, recompiles the messages catalogs of an instance. 
   This is automatically done while upgrading.
 
 See also chapter :ref:`internationalisation`.
--- a/doc/book/en/annexes/faq.rst	Wed Jun 03 09:09:33 2009 +0200
+++ b/doc/book/en/annexes/faq.rst	Wed Jun 03 09:15:20 2009 +0200
@@ -45,17 +45,16 @@
   we use standard OOP techniques and this is a key factor in a
   robust application.
 
-Why do you use the GPL license to prevent me from doing X ?
+Why do you use the LGPL license to prevent me from doing X ?
 -----------------------------------------------------------
 
-  GPL means that *if* you redistribute your application, you need to
-  redistribute it *and* the changes you made *and* the code _linked_
-  to it under the GPL licence.
+  LGPL means that *if* you redistribute your application, you need to
+  redistribute the changes you made to CubicWeb under the LGPL licence.
 
   Publishing a web site has nothing to do with redistributing
-  source code. A fair amount of companies use modified GPL code
+  source code. A fair amount of companies use modified LGPL code
   for internal use. And someone could publish a `CubicWeb` component
-  under a BSD licence for others to plug into a GPL framework without
+  under a BSD licence for others to plug into a LGPL framework without
   any problem. The only thing we are trying to prevent here is someone
   taking the framework and packaging it as closed source to his own
   clients.
@@ -64,10 +63,9 @@
 CubicWeb looks pretty recent. Is it stable ?
 --------------------------------------------
 
-  It is constantly evolving, piece by piece.  The framework has
-  evolved over the past seven years and data has been migrated from
-  one schema to the other ever since. There is a well-defined way to
-  handle data and schema migration.
+  It is constantly evolving, piece by piece.  The framework has evolved since
+  2001 and data has been migrated from one schema to the other ever since. There
+  is a well-defined way to handle data and schema migration.
 
 Why is the RQL query language looking similar to X ?
 -----------------------------------------------------
@@ -96,7 +94,46 @@
 
 which ajax library
 ------------------
-  [we use jquery and things on top of that]
+[we use jquery and things on top of that]
+
+
+How to implement security?
+--------------------------
+
+  This is an example of how it works in our framework::
+
+    class Version(EntityType):
+    """a version is defining the content of a particular project's
+    release"""
+    # definition of attributes is voluntarily missing
+    permissions = {'read': ('managers', 'users', 'guests',),
+                   'update': ('managers', 'logilab', 'owners',),
+                   'delete': ('managers', ),
+                   'add': ('managers', 'logilab',
+                        ERQLExpression('X version_of PROJ, U in_group G, PROJ
+                        require_permission P, P name "add_version", P require_group G'),)}
+
+  The above means that permission to read a Version is granted to any
+  user that is part of one of the groups 'managers', 'users', 'guests'.
+  The 'add' permission is granted to users in group 'managers' or
+  'logilab' and to users in group G, if G is linked by a permission
+  entity named "add_version" to the version's project.
+  ::
+
+    class version_of(RelationType):
+        """link a version to its project. A version is necessarily linked
+        to one and only one project. """
+        # some lines voluntarily missing
+        permissions = {'read': ('managers', 'users', 'guests',), 
+                       'delete': ('managers', ),
+                       'add': ('managers', 'logilab',
+                            RRQLExpression('O require_permission P, P name "add_version",
+                            'U in_group G, P require_group G'),) }
+
+  You can find additional information in the section :ref:`security`.
+
+  [XXX what does the second example means in addition to the first one?]
+
 
 `Error while publishing rest text ...`
 --------------------------------------
@@ -204,6 +241,7 @@
 
      where DATADIR is ``mycubes/data``.
 
+
 How to configure LDAP source?
 -------------------------------
 
@@ -251,7 +289,7 @@
 
   If your schema has an attribute of type Date or Datetime, you might
   want to format it. First, you should define your preferred format using
-  the site configuration panel ``http://appurl/view?vid=systemepropertiesform``
+  the site configuration panel ``http://appurl/view?vid=systempropertiesform``
   and then set ``ui.date`` and/or ``ui.datetime``.
   Then in the view code, use::
     
@@ -298,40 +336,19 @@
   mapping Google Accounts to local Euser entities automatically]
 
 
-How to implement security?
---------------------------
-
-  This is an example of how it works in our framework::
+How to reset the password for user joe?
+---------------------------------------
 
-    class Version(EntityType):
-    """a version is defining the content of a particular project's
-    release"""
-    # definition of attributes is voluntarily missing
-    permissions = {'read': ('managers', 'users', 'guests',),
-                   'update': ('managers', 'logilab', 'owners',),
-                   'delete': ('managers', ),
-                   'add': ('managers', 'logilab',
-                        ERQLExpression('X version_of PROJ, U in_group G, PROJ
-                        require_permission P, P name "add_version", P require_group G'),)}
+  You need to generate a new encrypted password::
 
-  The above means that permission to read a Version is granted to any
-  user that is part of one of the groups 'managers', 'users', 'guests'.
-  The 'add' permission is granted to users in group 'managers' or
-  'logilab' and to users in group G, if G is linked by a permission
-  entity named "add_version" to the version's project.
-  ::
+    $ python
+    >>> from cubicweb.server.utils import crypt_password
+    >>> crypt_password('joepass')
+    'qHO8282QN5Utg'
+    >>> 
 
-    class version_of(RelationType):
-        """link a version to its project. A version is necessarily linked
-        to one and only one project. """
-        # some lines voluntarily missing
-        permissions = {'read': ('managers', 'users', 'guests',),
-                       'delete': ('managers', ),
-                       'add': ('managers', 'logilab',
-                            RRQLExpression('O require_permission P, P name "add_version",
-                            'U in_group G, P require_group G'),) }
+  and paste it in the database::
 
-  You can find additional information in the section :ref:`security`.
-
-  [XXX what does the second example means in addition to the first one?]
-
+    $ psql mydb
+    mydb=> update cw_cwuser set cw_upassword='qHO8282QN5Utg' where cw_login='joe';
+    UPDATE 1
\ No newline at end of file
--- a/doc/book/en/conf.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/doc/book/en/conf.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,11 @@
 # -*- coding: utf-8 -*-
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 #
 # Cubicweb documentation build configuration file, created by
 # sphinx-quickstart on Fri Oct 31 09:10:36 2008.
@@ -53,7 +60,7 @@
 today_fmt = '%B %d, %Y'
 
 # List of documents that shouldn't be included in the build.
-#unused_docs = []
+unused_docs = ['D070-modules-cbw-api.en',]
 
 # List of directories, relative to source directories, that shouldn't be searched
 # for source files.
--- a/doc/book/en/development/devweb/internationalization.rst	Wed Jun 03 09:09:33 2009 +0200
+++ b/doc/book/en/development/devweb/internationalization.rst	Wed Jun 03 09:15:20 2009 +0200
@@ -78,17 +78,17 @@
 following commands for this purpose:
 
 
-* `i18nlibupdate` updates Cubicweb framework's translation
+* `i18ncubicweb` updates Cubicweb framework's translation
   catalogs. Unless you work on the framework development, you don't
   need to use this command.
 
-* `i18nupdate` updates the translation catalogs of *one particular
+* `i18ncube` updates the translation catalogs of *one particular
   component* (or of all components). After this command is
   executed you must update the translation files *.po* in the "i18n"
   directory of your template. This command will of course not remove
   existing translations still in use.
 
-* `i18ncompile` recompile the translation catalogs of *one particular
+* `i18ninstance` recompile the translation catalogs of *one particular
   instance* (or of all instances) after the translation catalogs of
   its components have been updated. This command is automatically
   called every time you create or update your instance. The compiled
@@ -103,8 +103,8 @@
 (after creating a new view or modifying the application's schema for exemple).
 To update the translation catalogs you need to do:
 
-1. `cubicweb-ctl i18nupdate <component>`
+1. `cubicweb-ctl i18ncube <component>`
 2. Edit the <component>/xxx.po  files and add missing translations (empty `msgstr`)
 3. `hg ci -m "updated i18n catalogs"`
-4. `cubicweb-ctl i18ncompile <myapplication>`
+4. `cubicweb-ctl i18ninstance <myapplication>`
 
--- a/doc/book/en/index.rst	Wed Jun 03 09:09:33 2009 +0200
+++ b/doc/book/en/index.rst	Wed Jun 03 09:15:20 2009 +0200
@@ -29,8 +29,6 @@
 
 The hacker will join development at the forge_.
 
-The impatient will go strait away to :ref:`QuickInstall`.
-
 The impatient developper will move right away to :ref:`SetUpEnv`.
 
 .. _Logilab: http://www.logilab.fr/
--- a/doc/book/en/intro/tutorial/components.rst	Wed Jun 03 09:09:33 2009 +0200
+++ b/doc/book/en/intro/tutorial/components.rst	Wed Jun 03 09:15:20 2009 +0200
@@ -70,7 +70,7 @@
 database with your model. For this purpose, `CubicWeb` provides
 a very useful command ``cubicweb-ctl shell blogdemo`` which
 launches an interactive migration Python shell. (see 
-:ref:`cubicweb-ctl-shell` for more details))
+:ref:`cubicweb-ctl` for more details))
 As you modified a relation from the `BlogEntry` schema,
 run the following command:
 ::
--- a/doc/book/en/intro/tutorial/create-cube.rst	Wed Jun 03 09:09:33 2009 +0200
+++ b/doc/book/en/intro/tutorial/create-cube.rst	Wed Jun 03 09:15:20 2009 +0200
@@ -3,6 +3,10 @@
 Create your cube
 ----------------
 
+The packages ``cubicweb`` and ``cubicweb-dev`` installs a command line tool
+for `CubicWeb` called ``cubicweb-ctl``. This tool provides a wide range of
+commands described in details in :ref:`cubicweb-ctl`. 
+
 Once your `CubicWeb` development environment is set up, you can create a new
 cube::
 
--- a/doc/book/fr/03-03-cubicweb-ctl.fr.txt	Wed Jun 03 09:09:33 2009 +0200
+++ b/doc/book/fr/03-03-cubicweb-ctl.fr.txt	Wed Jun 03 09:15:20 2009 +0200
@@ -71,9 +71,9 @@
 
 Commandes pour la maintenance des catalogues i18n
 -------------------------------------------------
-* ``i18nlibupdate``, regénère les catalogues de messages de la librairie CubicWeb
-* ``i18nupdate``, regénère les catalogues de messages d'un composant
-* ``i18ncompile``, recompile les catalogues de messages d'une instance. Cela est
+* ``i18ncubicweb``, regénère les catalogues de messages de la librairie CubicWeb
+* ``i18ncube``, regénère les catalogues de messages d'un composant
+* ``i18ninstance``, recompile les catalogues de messages d'une instance. Cela est
   effectué automatiquement lors d'une upgrade
 
 Cf :ref:`Internationalisation`.
--- a/doc/book/fr/12-internationalization.fr.txt	Wed Jun 03 09:09:33 2009 +0200
+++ b/doc/book/fr/12-internationalization.fr.txt	Wed Jun 03 09:15:20 2009 +0200
@@ -41,17 +41,17 @@
 Une fois l'application rendu internationalisable coté code, reste à gérer les
 catalogues de traductions. erudi-ctl intègre pour cela les commandes suivantes : 
 
-* `i18nlibupdate`, met à jour les catalogues de messages *de la librairie
+* `i18ncubicweb`, met à jour les catalogues de messages *de la librairie
   erudi*. Sauf si vous développez sur le framework (et non votre propre
   application), vous ne devriez pas avoir à utiliser cette commande
 
-* `i18nupdate`, met à jour les catalogues de messages *du composant* (ou de tous
+* `i18ncube`, met à jour les catalogues de messages *du composant* (ou de tous
   les composants). A la suite de cette commande, vous devez mettre à jour les
   fichiers de traduction *.po* dans le sous-répertoire "i18n" de votre
   template. Évidemment les traductions précédentes toujours utilisées ont été
   conservées.
 
-* `i18ncompile`, recompile les catalogues de messages *d'une instance* (ou de
+* `i18ninstance`, recompile les catalogues de messages *d'une instance* (ou de
   toutes les instances) après mise à jour des catalogues de son composant. Cela
   est effectué automatiquement lors d'une création ou d'une mise à jour. Les
   catalogues de messages compilés se trouvent dans le répertoire
@@ -65,9 +65,9 @@
 application (en ajoutant une nouvelle vue ou en ayant modifié le schéma par
 exemple) :
 
-1. `cubicweb-ctl i18nupdate <composant>`
+1. `cubicweb-ctl i18ncube <composant>`
 2. éditer les fichiers <composant>/xxx.po dans pour y rajouter les traductions
    manquantes (`msgstr` vide) 
 3. `hg ci -m "updated i18n catalogs"`
-4. `cubicweb-ctl i18n compile <monapplication>`
+4. `cubicweb-ctl i18ninstance <monapplication>`
 
--- a/doc/book/fr/19-i18n.fr.txt	Wed Jun 03 09:09:33 2009 +0200
+++ b/doc/book/fr/19-i18n.fr.txt	Wed Jun 03 09:15:20 2009 +0200
@@ -39,17 +39,17 @@
 Une fois l'application rendu internationalisable coté code, reste à gérer les
 catalogues de traductions. cubicweb-ctl intègre pour cela les commandes suivantes : 
 
-* `i18nlibupdate`, met à jour les catalogues de messages *de la librairie
+* `i18ncubicweb`, met à jour les catalogues de messages *de la librairie
   cubicweb*. Sauf si vous développez sur le framework (et non votre propre
   application), vous ne devriez pas avoir à utiliser cette commande
 
-* `i18nupdate`, met à jour les catalogues de messages *du composant* (ou de tous
+* `i18ncube`, met à jour les catalogues de messages *du composant* (ou de tous
   les composants). A la suite de cette commande, vous devez mettre à jour les
   fichiers de traduction *.po* dans le sous-répertoire "i18n" de votre
   template. Évidemment les traductions précédentes toujours utilisées ont été
   conservées.
 
-* `i18ncompile`, recompile les catalogues de messages *d'une instance* (ou de
+* `i18ninstance`, recompile les catalogues de messages *d'une instance* (ou de
   toutes les instances) après mise à jour des catalogues de son composant. Cela
   est effectué automatiquement lors d'une création ou d'une mise à jour. Les
   catalogues de messages compilés se trouvent dans le répertoire
@@ -63,9 +63,9 @@
 application (en ajoutant une nouvelle vue ou en ayant modifié le schéma par
 exemple) :
 
-1. `cubicweb-ctl i18nupdate <composant>`
+1. `cubicweb-ctl i18ncube <composant>`
 2. éditer les fichiers <composant>/xxx.po dans pour y rajouter les traductions
    manquantes (`msgstr` vide) 
 3. `hg ci -m "updated i18n catalogs"`
-4. `cubicweb-ctl i18n compile <monapplication>`
+4. `cubicweb-ctl i18ninstance <monapplication>`
 
--- a/doc/book/fr/conf.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/doc/book/fr/conf.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,11 @@
 # -*- coding: utf-8 -*-
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 #
 # Cubicweb documentation build configuration file, created by
 # sphinx-quickstart on Fri Oct 31 09:10:36 2008.
@@ -36,7 +43,7 @@
 
 # General substitutions.
 project = 'Cubicweb'
-copyright = '2008, Logilab'
+copyright = '2008-2009, Logilab'
 
 # The default replacements for |version| and |release|, also used in various
 # other places throughout the built documents.
--- a/doc/book/mode_plan.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/doc/book/mode_plan.py	Wed Jun 03 09:15:20 2009 +0200
@@ -5,6 +5,7 @@
 >>> ren('A01','A03')
 rename A010-joe.en.txt to A030-joe.en.txt
 accept [y/N]?
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 def ren(a,b):
--- a/doc/tools/generate_modules.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/doc/tools/generate_modules.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,99 +1,18 @@
-import sys
+"""generate list of modules for sphinx doc
 
-"""
-Generates the chapter that list all the modules in CubicWeb
-in order to pull all the docstring.
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
-class ModuleGenerator:
-    HEADER = """.. -*- coding: utf-8 -*-
-
-============
-CubicWeb API
-============
-"""
-    EXCLUDE_DIRS = ('test', 'tests', 'examples', 'data', 'doc', '.hg', 'migration')  
-
-    def __init__(self, output_fn, mod_names):
-        self.mod_names =  mod_names
-        self.fn = open(output_fn, 'w')
-        self.fn.write(self.HEADER)
-
-    def done(self):
-        self.fn.close()
-        
-    def gen_module(self, mod_name):
-        mod_entry = """
-:mod:`%s`
-%s
+import sys
 
-.. automodule:: %s
-   :members:
-""" % (mod_name, '='*(len(':mod:``'+mod_name)), mod_name)
-        self.fn.write(mod_entry)
-
-    def find_modules(self):
-        import os
-        modules = []
-        for mod_name in self.mod_names:
-            for root, dirs, files in os.walk(mod_name):
-                if self.keep_module(root):
-                    for name in files:
-                        if name == "__init__.py":
-                            if self.format_mod_name(root, mod_name) not in modules:
-                                modules.append(self.format_mod_name(root, mod_name))
-                        else:
-                            if name.endswith(".py") and name != "__pkginfo__.py" and "__init__.py" in files:
-                                filename = root + '/' + name.split('.py')[0]
-                                if self.format_mod_name(filename, mod_name) not in modules:
-                                    modules.append(self.format_mod_name(filename, mod_name))
-        return modules
+EXCLUDE_DIRS = ('test', 'tests', 'examples', 'data', 'doc', '.hg', 'migration')
+if __name__ == '__main__':
 
-    def gen_modules(self):
-        for module in self.find_modules():
-            self.gen_module(module)
-
-    def format_mod_name(self, path, mod_name):
-        mod_root = mod_name.split('/')[-1]
-        mod_end = path.split(mod_root)[-1]
-        return mod_root + mod_end.replace('/', '.')
-
-    def keep_module(self, mod_end):
-        """
-        Filter modules in order to exclude specific package directories.
-        """
-        for dir in self.EXCLUDE_DIRS:
-            if mod_end.find(dir) != -1:
-                return False
-        return True
-
-USAGE = """
-Two arguments required:
-    generate_modules [cubicweb-root] [file-out]
+    from logilab.common.sphinxutils import generate_modules_file
 
-[cubicweb-root] : full path to cubicweb forest
-[file-out] : rest file containing the list of modules for Sphinx
-"""
-def generate_modules_file(args):
-    if len(args) != 2:
-        print USAGE
-        sys.exit()
-    CW_ROOT = args[0]
-    OUTPUT = args[1]
-    modules = (CW_ROOT + '/cubicweb', \
-               CW_ROOT + '/indexer', \
-               CW_ROOT + '/logilab', \
-               CW_ROOT + '/rql', \
-               CW_ROOT + '/yams')
-
-    mg = ModuleGenerator(CW_ROOT + '/cubicweb/doc/book/en/' + OUTPUT, modules)
-    mg.find_modules()
-    mg.gen_modules()
-    mg.done()
-    print args
-
-
-
-if __name__ == '__main__':
-    generate_modules_file(sys.argv[1:])
-
+    gen = generate_modules_file(sys.argv[1:])
+    gen.set_docdir("cubicweb/doc/book/en")
+    gen.make(['cubicweb', '/indexer', '/logilab', '/rql', '/yams'], EXCLUDE_DIRS)
--- a/entities/__init__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/entities/__init__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """base application's entities class implementation: `AnyEntity`
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -57,16 +58,6 @@
             return '%s DESC' % var
         return None
 
-    @classmethod
-    def __initialize__(cls):
-        super(ANYENTITY, cls).__initialize__() # XXX
-        # set a default_ATTR method for rich text format fields
-        # XXX move this away once the old widgets have been dropped!
-        eschema = cls.e_schema
-        for metaattr, (metadata, attr) in eschema.meta_attributes().iteritems():
-            if metadata == 'format' and not hasattr(cls, 'default_%s' % metaattr):
-                setattr(cls, 'default_%s' % metaattr, cls._default_format)
-
     # meta data api ###########################################################
 
     def dc_title(self):
@@ -251,13 +242,13 @@
 
     @obsolete('use EntityFieldsForm.subject_relation_vocabulary')
     def subject_relation_vocabulary(self, rtype, limit):
-        from cubicweb.web.form import EntityFieldsForm
-        return EntityFieldsForm(self.req, None, entity=self).subject_relation_vocabulary(rtype, limit)
+        form = self.vreg.select_object('forms', 'edition', self.req, entity=self)
+        return form.subject_relation_vocabulary(rtype, limit)
 
     @obsolete('use EntityFieldsForm.object_relation_vocabulary')
     def object_relation_vocabulary(self, rtype, limit):
-        from cubicweb.web.form import EntityFieldsForm
-        return EntityFieldsForm(self.req, None, entity=self).object_relation_vocabulary(rtype, limit)
+        form = self.vreg.select_object('forms', 'edition', self.req, entity=self)
+        return form.object_relation_vocabulary(rtype, limit)
 
     @obsolete('use AutomaticEntityForm.[e]relations_by_category')
     def relations_by_category(self, categories=None, permission=None):
@@ -269,9 +260,6 @@
         from cubicweb.web.views.autoform import AutomaticEntityForm
         return AutomaticEntityForm.esrelations_by_category(self, categories, permission)
 
-    def _default_format(self):
-        return self.req.property_value('ui.default-text-format')
-
     def attribute_values(self, attrname):
         if self.has_eid() or attrname in self:
             try:
--- a/entities/authobjs.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/entities/authobjs.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """entity classes user and group entities
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/entities/lib.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/entities/lib.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """entity classes for optional library entities
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/entities/schemaobjs.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/entities/schemaobjs.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """schema definition related entities
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -19,8 +20,8 @@
     fetch_attrs, fetch_order = fetch_config(['name'])
 
     def dc_title(self):
-        return self.req._(self.name)
-    
+        return u'%s (%s)' % (self.name, self.req._(self.name))
+
     def dc_long_title(self):
         stereotypes = []
         _ = self.req._
@@ -40,10 +41,10 @@
 class CWRType(AnyEntity):
     id = 'CWRType'
     fetch_attrs, fetch_order = fetch_config(['name'])
-    
+
     def dc_title(self):
-        return self.req._(self.name)
-    
+        return u'%s (%s)' % (self.name, self.req._(self.name))
+
     def dc_long_title(self):
         stereotypes = []
         _ = self.req._
@@ -61,7 +62,7 @@
 
     def inlined_changed(self, inlined):
         """check inlining is necessary and possible:
-        
+
         * return False if nothing has changed
         * raise ValidationError if inlining is'nt possible
         * eventually return True
@@ -88,13 +89,13 @@
 class CWRelation(AnyEntity):
     id = 'CWRelation'
     fetch_attrs = fetch_config(['cardinality'])[0]
-    
+
     def dc_title(self):
         return u'%s %s %s' % (
             self.from_entity[0].name,
-            self.relation_type[0].name, 
+            self.relation_type[0].name,
             self.to_entity[0].name)
-    
+
     def dc_long_title(self):
         card = self.cardinality
         scard, ocard = u'', u''
@@ -118,7 +119,7 @@
 
 class CWAttribute(CWRelation):
     id = 'CWAttribute'
-    
+
     def dc_long_title(self):
         card = self.cardinality
         scard = u''
@@ -126,7 +127,7 @@
             scard = '+'
         return u'%s %s%s %s' % (
             self.from_entity[0].name,
-            scard, self.relation_type[0].name, 
+            scard, self.relation_type[0].name,
             self.to_entity[0].name)
 
 
@@ -136,7 +137,7 @@
 
     def dc_title(self):
         return '%s(%s)' % (self.cstrtype[0].name, self.value or u'')
-        
+
     def after_deletion_path(self):
         """return (path, parameters) which should be used as redirect
         information when this entity is being deleted
@@ -149,7 +150,7 @@
     def type(self):
         return self.cstrtype[0].name
 
-        
+
 class RQLExpression(AnyEntity):
     id = 'RQLExpression'
     fetch_attrs, fetch_order = fetch_config(['exprtype', 'mainvars', 'expression'])
@@ -164,17 +165,17 @@
             values = getattr(self, 'reverse_%s' % rel)
             if values:
                 return values[0]
-            
+
     @cached
     def _rqlexpr(self):
         if self.exprtype == 'ERQLExpression':
             return ERQLExpression(self.expression, self.mainvars, self.eid)
         #if self.exprtype == 'RRQLExpression':
         return RRQLExpression(self.expression, self.mainvars, self.eid)
-    
+
     def check_expression(self, *args, **kwargs):
         return self._rqlexpr().check(*args, **kwargs)
-    
+
     def after_deletion_path(self):
         """return (path, parameters) which should be used as redirect
         information when this entity is being deleted
@@ -192,7 +193,7 @@
         if self.label:
             return '%s (%s)' % (self.req._(self.name), self.label)
         return self.req._(self.name)
-    
+
     def after_deletion_path(self):
         """return (path, parameters) which should be used as redirect
         information when this entity is being deleted
--- a/entities/test/data/schema.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/entities/test/data/schema.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 class Company(EntityType):
     name = String()
 
--- a/entities/test/unittest_base.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/entities/test/unittest_base.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,5 +1,11 @@
 # -*- coding: utf-8 -*-
-"""unit tests for cubicweb.entities.base module"""
+"""unit tests for cubicweb.entities.base module
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 from logilab.common.testlib import unittest_main
 from logilab.common.decorators import clear_cache
--- a/entities/wfobjs.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/entities/wfobjs.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """workflow definition and history related entities
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/entity.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/entity.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Base class for entity objects manipulated in clients
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -123,7 +124,7 @@
             for name, widgets in _get_defs('widgets', name, bases, classdict):
                 warn('%s: widgets is deprecated' % name, DeprecationWarning)
                 for rtype, wdgname in widgets.iteritems():
-                    if wdgname in ('URLWidget', 'EmbededURLWidget'):
+                    if wdgname in ('URLWidget', 'EmbededURLWidget', 'RawDynamicComboBoxWidget'):
                         warn('%s widget is deprecated' % wdgname, DeprecationWarning)
                         continue
                     if wdgname == 'StringWidget':
@@ -284,6 +285,23 @@
         parents.append(cls.vreg.etype_class('Any'))
         return parents
 
+    @classmethod
+    @cached
+    def _rest_attr_info(cls):
+        mainattr, needcheck = 'eid', True
+        if cls.rest_attr:
+            mainattr = cls.rest_attr
+            needcheck = not cls.e_schema.has_unique_values(mainattr)
+        else:
+            for rschema in cls.e_schema.subject_relations():
+                if rschema.is_final() and rschema != 'eid' and cls.e_schema.has_unique_values(rschema):
+                    mainattr = str(rschema)
+                    needcheck = False
+                    break
+        if mainattr == 'eid':
+            needcheck = False
+        return mainattr, needcheck
+
     def __init__(self, req, rset=None, row=None, col=0):
         AppRsetObject.__init__(self, req, rset, row, col)
         dict.__init__(self)
@@ -362,47 +380,41 @@
         # XXX search_state is web specific
         if getattr(self.req, 'search_state', ('normal',))[0] == 'normal':
             kwargs['base_url'] = self.metainformation()['source'].get('base-url')
-        if method is None or method == 'view':
-            kwargs['_restpath'] = self.rest_path()
+        if method in (None, 'view'):
+            try:
+                kwargs['_restpath'] = self.rest_path(kwargs.get('base_url'))
+            except TypeError:
+                warn('%s: rest_path() now take use_ext_eid argument, '
+                     'please update' % self.id, DeprecationWarning)
+                kwargs['_restpath'] = self.rest_path()
         else:
             kwargs['rql'] = 'Any X WHERE X eid %s' % self.eid
         return self.build_url(method, **kwargs)
 
-    def rest_path(self):
+    def rest_path(self, use_ext_eid=False):
         """returns a REST-like (relative) path for this entity"""
         mainattr, needcheck = self._rest_attr_info()
         etype = str(self.e_schema)
-        if mainattr == 'eid':
-            value = self.eid
-        else:
+        path = etype.lower()
+        if mainattr != 'eid':
             value = getattr(self, mainattr)
             if value is None:
-                return '%s/eid/%s' % (etype.lower(), self.eid)
-        if needcheck:
-            # make sure url is not ambiguous
-            rql = 'Any COUNT(X) WHERE X is %s, X %s %%(value)s' % (etype, mainattr)
-            if value is not None:
+                mainattr = 'eid'
+                path += '/eid'
+            elif needcheck:
+                # make sure url is not ambiguous
+                rql = 'Any COUNT(X) WHERE X is %s, X %s %%(value)s' % (
+                    etype, mainattr)
                 nbresults = self.req.execute(rql, {'value' : value})[0][0]
-                # may an assertion that nbresults is not 0 would be a good idea
-                if nbresults != 1: # no ambiguity
-                    return '%s/eid/%s' % (etype.lower(), self.eid)
-        return '%s/%s' % (etype.lower(), self.req.url_quote(value))
-
-    @classmethod
-    def _rest_attr_info(cls):
-        mainattr, needcheck = 'eid', True
-        if cls.rest_attr:
-            mainattr = cls.rest_attr
-            needcheck = not cls.e_schema.has_unique_values(mainattr)
-        else:
-            for rschema in cls.e_schema.subject_relations():
-                if rschema.is_final() and rschema != 'eid' and cls.e_schema.has_unique_values(rschema):
-                    mainattr = str(rschema)
-                    needcheck = False
-                    break
+                if nbresults != 1: # ambiguity?
+                    mainattr = 'eid'
+                    path += '/eid'
         if mainattr == 'eid':
-            needcheck = False
-        return mainattr, needcheck
+            if use_ext_eid:
+                value = self.metainformation()['extid']
+            else:
+                value = self.eid
+        return '%s/%s' % (path, self.req.url_quote(value))
 
     def attr_metadata(self, attr, metadata):
         """return a metadata for an attribute (None if unspecified)"""
@@ -712,9 +724,8 @@
         If `eid` is None in one of these couples, it should be
         interpreted as a separator in case vocabulary results are grouped
         """
-        from cubicweb.web.form import EntityFieldsForm
         from logilab.common.testlib import mock_object
-        form = EntityFieldsForm(self.req, entity=self)
+        form = self.vreg.select_object('forms', 'edition', self.req, entity=self)
         field = mock_object(name=rtype, role=role)
         return form.form_field_vocabulary(field, limit)
 
--- a/etwist/__init__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/etwist/__init__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,2 +1,8 @@
-""" CW - nevow/twisted client """
+""" CW - nevow/twisted client 
 
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
+
--- a/etwist/request.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/etwist/request.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Twisted request handler for CubicWeb
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -39,11 +40,11 @@
     def base_url(self):
         """return the root url of the application"""
         return self._base_url
-    
+
     def http_method(self):
         """returns 'POST', 'GET', 'HEAD', etc."""
         return self._twreq.method
-    
+
     def relative_path(self, includeparams=True):
         """return the normalized path of the request (ie at least relative
         to the application's root, but some other normalization may be needed
--- a/etwist/server.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/etwist/server.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """twisted server for CubicWeb web applications
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -196,7 +197,7 @@
         if self.url_rewriter is not None:
             # XXX should occur before authentication?
             try:
-                path = self.url_rewriter.rewrite(host, origpath)
+                path = self.url_rewriter.rewrite(host, origpath, req)
             except Redirect, ex:
                 return self.redirect(req, ex.location)
             request.uri.replace(origpath, path, 1)
--- a/etwist/test/unittest_server.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/etwist/test/unittest_server.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from cubicweb.devtools.apptest import EnvBasedTC
 from cubicweb.etwist.server import host_prefixed_baseurl
 
--- a/etwist/twconfig.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/etwist/twconfig.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,14 +2,15 @@
 
 * the "twisted" configuration to get a web application running in a standalone
   twisted web server which talk to a repository server using Pyro
-  
+
 * the "all-in-one" configuration to get a web application running in a twisted
   web server integrating a repository server in the same process (only available
   if the repository part of the software is installed
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -63,12 +64,12 @@
         ('pyro-server',
          {'type' : 'yn',
           # pyro is only a recommends by default, so don't activate it here
-          'default': False, 
+          'default': False,
           'help': 'run a pyro server',
           'group': 'main', 'inputlevel': 1,
           }),
         ) + WebConfiguration.options)
-    
+
     def server_file(self):
         return join(self.apphome, '%s-%s.py' % (self.appid, self.name))
 
@@ -91,6 +92,6 @@
         def pyro_enabled(self):
             """tell if pyro is activated for the in memory repository"""
             return self['pyro-server']
-        
+
 except ImportError:
     pass
--- a/etwist/twctl.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/etwist/twctl.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,9 @@
 """cubicweb-clt handlers for twisted
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 import sys
@@ -52,8 +57,8 @@
 class TWStopHandler(CommandHandler):
     cmdname = 'stop'
     cfgname = 'twisted'
-    
-    
+
+
 try:
     from cubicweb.server import serverctl
 
@@ -67,7 +72,7 @@
             """bootstrap this configuration"""
             serverctl.RepositoryCreateHandler.bootstrap(self, cubes, inputlevel)
             TWCreateHandler.bootstrap(self, cubes, inputlevel)
-            
+
     class AllInOneStartHandler(TWStartHandler):
         cmdname = 'start'
         cfgname = 'all-in-one'
@@ -80,4 +85,3 @@
 
 except ImportError:
     pass
-    
--- a/ext/html4zope.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/ext/html4zope.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,11 @@
 # Author: David Goodger
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 # Contact: goodger@users.sourceforge.net
 # Revision: $Revision: 1.2 $
 # Date: $Date: 2005-07-04 16:36:50 $
@@ -40,19 +47,19 @@
     def __init__(self, url, klass):
         self.base_url = url
         self.translator_class = HTMLTranslator
-        
+
     def __call__(self, document):
         translator = self.translator_class(document)
         translator.base_url = self.base_url
         return translator
-    
+
 class HTMLTranslator(CSS1HTMLTranslator):
     """ReST tree to html translator"""
 
     def astext(self):
         """return the extracted html"""
         return ''.join(self.body)
-    
+
     def visit_title(self, node):
         """Only 6 section levels are supported by HTML."""
         if isinstance(node.parent, nodes.topic):
@@ -124,7 +131,7 @@
 
     def depart_problematic(self, node):
         pass
-    
+
     def visit_system_message(self, node):
         backref_text = ''
         if len(node['backrefs']):
--- a/ext/rest.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/ext/rest.py	Wed Jun 03 09:15:20 2009 +0200
@@ -13,8 +13,9 @@
 * `sourcecode` (if pygments is installed), source code colorization
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/ext/tal.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/ext/tal.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """provides simpleTAL extensions for CubicWeb
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 __docformat__ = "restructuredtext en"
@@ -12,7 +13,7 @@
 from os.path import exists, isdir, join
 from logging import getLogger
 from StringIO import StringIO
-        
+
 from simpletal import simpleTAL, simpleTALES
 
 from logilab.common.decorators import cached
@@ -23,7 +24,7 @@
 class LoggerAdapter(object):
     def __init__(self, tal_logger):
         self.tal_logger = tal_logger
-        
+
     def debug(self, msg):
         LOGGER.debug(msg)
 
@@ -49,7 +50,7 @@
         simpleTALES.Context.addRepeat(self, name, var, initialValue)
 
 # XXX FIXME need to find a clean to define OPCODE values for extensions
-I18N_CONTENT = 18  
+I18N_CONTENT = 18
 I18N_REPLACE = 19
 RQL_EXECUTE  = 20
 # simpleTAL uses the OPCODE values to define priority over commands.
@@ -113,7 +114,7 @@
         """
         # Compile tal:attributes into attribute command
         # Argument: [(attributeName, expression)]
-        
+
         # Break up the list of attribute settings first
         commandArgs = []
         # We only want to match semi-colons that are not escaped
@@ -187,7 +188,7 @@
 def compile_template_file(filepath):
     """compiles a TAL template file
     :type filepath: str
-    :param template: path of the file to compile 
+    :param template: path of the file to compile
     """
     fp = file(filepath)
     file_content = unicode(fp.read()) # template file should be pure ASCII
@@ -253,4 +254,3 @@
                 return compile_template_file(filepath)
         raise Exception('no such template %s' % self.filename)
     _compiled_template = cached(_compiled_template, 0)
-    
--- a/ext/test/unittest_rest.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/ext/test/unittest_rest.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from logilab.common.testlib import unittest_main
 from cubicweb.devtools.apptest import EnvBasedTC
 
--- a/gettext.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/gettext.py	Wed Jun 03 09:15:20 2009 +0200
@@ -8,6 +8,7 @@
 languages.  L10N refers to the adaptation of your program, once
 internationalized, to the local language and cultural habits.
 
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 # This module represents the integration of work, contributions, feedback, and
--- a/goa/__init__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/__init__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """cubicweb on google appengine
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -17,7 +18,7 @@
     pass
 else:
 
-    import os    
+    import os
     _SS = os.environ.get('SERVER_SOFTWARE')
     if _SS is None:
         MODE = 'test'
@@ -30,7 +31,7 @@
     from cubicweb.goa.gaesource import GAESource
     SOURCE_TYPES['gae'] = GAESource
 
-    
+
     def do_monkey_patch():
 
         # monkey patch yams Bytes validator since it should take a bytes string with gae
@@ -56,16 +57,16 @@
 
         # XXX monkey patch cubicweb.schema.CubicWebSchema to have string eid with
         #     optional cardinality (since eid is set after the validation)
-        
+
         import re
         from yams import buildobjs as ybo
-        
+
         def add_entity_type(self, edef):
             edef.name = edef.name.encode()
             assert re.match(r'[A-Z][A-Za-z0-9]*[a-z]+[0-9]*$', edef.name), repr(edef.name)
             eschema = super(CubicWebSchema, self).add_entity_type(edef)
             if not eschema.is_final():
-                # automatically add the eid relation to non final entity types 
+                # automatically add the eid relation to non final entity types
                 rdef = ybo.RelationDefinition(eschema.type, 'eid', 'Bytes',
                                               cardinality='?1', uid=True)
                 self.add_relation_def(rdef)
@@ -73,7 +74,7 @@
                 self.add_relation_def(rdef)
             self._eid_index[eschema.eid] = eschema
             return eschema
-        
+
         from cubicweb.schema import CubicWebSchema
         CubicWebSchema.add_entity_type = add_entity_type
 
@@ -93,7 +94,7 @@
             cubes = config['included-cubes'] + config['included-yams-cubes']
             return config.expand_cubes(cubes)
         repository.Repository.get_cubes = get_cubes
-        
+
         from rql import RQLHelper
         RQLHelper.simplify = lambda x, r: None
 
--- a/goa/appobjects/components.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/appobjects/components.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """overrides some base views for cubicweb on google appengine
 
 :organization: Logilab
-:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -23,7 +24,7 @@
     to search for something to link to the edited eid
     """
     id = 'search-associate'
-    
+
     __select__ = one_line_rset() & match_search_state('linksearch') & accept
 
     def cell_call(self, row, col):
@@ -43,7 +44,7 @@
     binary = True
     content_type = 'image/png'
     def call(self):
-        """display global schema information"""        
+        """display global schema information"""
         skipmeta = not int(self.req.form.get('withmeta', 0))
         if skipmeta:
             url = self.build_url('data/schema.png')
--- a/goa/appobjects/dbmgmt.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/appobjects/dbmgmt.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 restoration).
 
 :organization: Logilab
-:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -54,8 +55,8 @@
         values.append('__session=%s' % cookie['__session'].value)
         self.w(u"<p>pass this flag to the client: --cookie='%s'</p>"
                % html_escape('; '.join(values)))
-        
-        
+
+
 
 class ContentInit(StartupView):
     """special management view to initialize content of a repository,
@@ -74,7 +75,7 @@
         status['stepid'] = stepid
         Put(status)
         self.msg(msg)
-        
+
     def call(self):
         status = _get_status('creation')
         if status.get('finished'):
@@ -149,7 +150,7 @@
                        '<b>delete all datastore content</b> so process can be '
                        'reinitialized</div>' % html_escape(self.req.base_url()))
         Put(status)
-        
+
     @property
     @cached
     def _migrhandler(self):
@@ -164,12 +165,12 @@
     def continue_link(self):
         self.w(u'<a href="%s">continue</a><br/>' % html_escape(self.req.url()))
 
-        
+
 class ContentClear(StartupView):
     id = 'contentclear'
     __select__ = none_rset() & match_user_groups('managers')
     skip_etypes = ('CWGroup', 'CWUser')
-    
+
     def call(self):
         # XXX should use unsafe_execute with all hooks deactivated
         # XXX step by catching datastore errors?
--- a/goa/appobjects/gauthservice.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/appobjects/gauthservice.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """authentication using google authentication service
 
 :organization: Logilab
-:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -23,7 +24,7 @@
 
     def url(self):
         return users.create_logout_url(self.req.build_url('logout') )
-    
+
 def registration_callback(vreg):
     if hasattr(vreg.config, 'has_resource'):
         vreg.register(GACWUserLink, clear=True)
--- a/goa/appobjects/sessions.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/appobjects/sessions.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,12 +1,13 @@
 """persistent sessions stored in big table
 
 :organization: Logilab
-:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 
 XXX TODO:
 * cleanup persistent session
 * use user as ancestor?
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -41,10 +42,10 @@
     def __init__(self, *args, **kwargs):
         super(GAEAuthenticationManager, self).__init__(*args, **kwargs)
         self._repo = self.config.repository(vreg=self.vreg)
-        
+
     def authenticate(self, req, _login=None, _password=None):
         """authenticate user and return an established connection for this user
-        
+
         :raise ExplicitLogin: if authentication is required (no authentication
         info found or wrong user/password)
         """
@@ -75,7 +76,7 @@
     def __init__(self, *args, **kwargs):
         super(GAEPersistentSessionManager, self).__init__(*args, **kwargs)
         self._repo = self.config.repository(vreg=self.vreg)
-        
+
     def get_session(self, req, sessionid):
         """return existing session for the given session identifier"""
         # search a record for the given session
@@ -126,7 +127,7 @@
         record['anonymous_connection'] = cnx.anonymous_connection
         Put(record)
         return self._get_proxy(req, record, cnx, user)
-    
+
     def close_session(self, proxy):
         """close session on logout or on invalid session detected (expired out,
         corrupted...)
@@ -136,7 +137,7 @@
     def current_sessions(self):
         for record in Query('CubicWebSession').Run():
             yield ConnectionProxy(record)
-            
+
     def _get_proxy(self, req, record, cnx, user):
         proxy = ConnectionProxy(record, cnx, user)
         user.req = req
@@ -145,7 +146,7 @@
 
 
 class ConnectionProxy(object):
-    
+
     def __init__(self, record, cnx=None, user=None):
         self.__record = record
         self.__cnx = cnx
@@ -153,7 +154,7 @@
         self.__data = None
         self.__is_dirty = False
         self.sessionid = record.key().name()[4:] # remove 'key_' prefix
-        
+
     def __repr__(self):
         sstr = '<ConnectionProxy %s' % self.sessionid
         if self.anonymous_connection:
@@ -162,7 +163,7 @@
             sstr += ' for %s' % self.__user.login
         sstr += ', last used %s>' % strftime('%T', localtime(self.last_usage_time))
         return sstr
-        
+
     def __getattribute__(self, name):
         try:
             return super(ConnectionProxy, self).__getattribute__(name)
@@ -174,7 +175,7 @@
         self.__record['last_usage_time'] = value
     def _get_last_usage_time(self):
         return self.__record['last_usage_time']
-    
+
     last_usage_time = property(_get_last_usage_time, _set_last_usage_time)
 
     @property
@@ -182,7 +183,7 @@
         # use get() for bw compat if sessions without anonymous information are
         # found. Set default to True to limit lifetime of those sessions.
         return self.__record.get('anonymous_connection', True)
-        
+
     @property
     @cached
     def data(self):
@@ -194,7 +195,7 @@
                 self.exception('corrupted session data for session %s',
                                self.__cnx)
         return {}
-        
+
     def get_session_data(self, key, default=None, pop=False):
         """return value associated to `key` in session data"""
         if pop:
@@ -206,20 +207,20 @@
                 return default
         else:
             return self.data.get(key, default)
-        
+
     def set_session_data(self, key, value):
         """set value associated to `key` in session data"""
         self.data[key] = value
         self.__is_dirty = True
-        
+
     def del_session_data(self, key):
         """remove value associated to `key` in session data"""
         try:
             del self.data[key]
             self.__is_dirty = True
         except KeyError:
-            pass    
-            
+            pass
+
     def commit(self):
         if self.__is_dirty:
             self.__save()
@@ -233,7 +234,7 @@
         if self.__cnx is not None:
             self.__cnx.close()
         Delete(self.__record)
-        
+
     def __save(self):
         if self.__is_dirty:
             self.__record['data'] = Blob(dumps(self.data))
@@ -243,7 +244,7 @@
     def user(self, req=None, props=None):
         """return the User object associated to this connection"""
         return self.__user
-        
+
 
 import logging
 from cubicweb import set_log_methods
@@ -256,7 +257,7 @@
 class SessionsCleaner(StartupView):
     id = 'cleansessions'
     __select__ = none_rset() & match_user_groups('managers')
-    
+
     def call(self):
         # clean web session
         session_manager = application.SESSION_MANAGER
@@ -270,7 +271,7 @@
         self.w(u'%s remaining sessions<br/>\n' % remaining)
         self.w(u'</div>')
 
-        
+
 def registration_callback(vreg):
     vreg.register(SessionsCleaner)
     vreg.register(GAEAuthenticationManager, clear=True)
--- a/goa/db.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/db.py	Wed Jun 03 09:15:20 2009 +0200
@@ -13,11 +13,11 @@
 * all methods returning `google.appengine.ext.db.Model` instance(s) will return
   `cubicweb.goa.db.Model` instance instead (though you should see almost no
   difference since those instances have the same api)
-  
+
 * class methods returning model instance take a `req` as first argument, unless
   they are called through an instance, representing the current request
   (accessible through `self.req` on almost all objects)
-  
+
 * XXX no instance.<modelname>_set attributes, use instance.reverse_<attr name>
       instead
 * XXX reference property always return a list of objects, not the instance
@@ -25,8 +25,9 @@
 * XXX ListProperty
 
 :organization: Logilab
-:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -47,7 +48,7 @@
 from google.appengine.api.datastore_errors import BadKeyError
 
 # XXX remove this dependancy
-from google.appengine.ext import db 
+from google.appengine.ext import db
 
 
 def rset_from_objs(req, objs, attrs=('eid',), rql=None, args=None):
@@ -76,15 +77,15 @@
                 value = obj[attr]
                 descr = str(eschema.destination(attr))
             line.append(value)
-            linedescr.append(descr)            
+            linedescr.append(descr)
         rows.append(line)
         description.append(linedescr)
         for j, attr in enumerate(attrs):
             if attr == 'eid':
                 entity = vreg.etype_class(eschema.type)(req, rset, i, j)
-                rset._get_entity_cache_ = {(i, j): entity}        
+                rset._get_entity_cache_ = {(i, j): entity}
     rset.rowcount = len(rows)
-    req.decorate_rset(rset)    
+    req.decorate_rset(rset)
     return rset
 
 
@@ -102,7 +103,7 @@
         return wrapped(cls, req, *args, **kwargs)
     return iclassmethod(wrapper)
 
-    
+
 class gaedbmetaentity(metaentity):
     """metaclass for goa.db.Model classes: filter entity / db model part,
     put aside the db model part for later creation of db model class.
@@ -139,15 +140,15 @@
 class Model(entities.AnyEntity):
     id = 'Any'
     __metaclass__ = gaedbmetaentity
-    
+
     row = col = 0
-    
+
     @classmethod
     def __initialize__(cls):
         super(Model, cls).__initialize__()
         cls._attributes = frozenset(rschema for rschema in cls.e_schema.subject_relations()
                                     if rschema.is_final())
-    
+
     def __init__(self, *args, **kwargs):
         # db.Model prototype:
         #   __init__(self, parent=None, key_name=None, **kw)
@@ -168,7 +169,7 @@
                         val = val._dbmodel
                     kwargs[key] = val.key()
             self._gaeinitargs = (args, kwargs)
-            
+
     def __repr__(self):
         return '<ModelEntity %s %s %s at %s>' % (
             self.e_schema, self.eid, self.keys(), id(self))
@@ -179,7 +180,7 @@
             tschema = self.e_schema.destination(attr)
             if tschema == 'String':
                 if len(value) > 500:
-                    value = Text(value)                
+                    value = Text(value)
             elif tschema == 'Password':
                 # if value is a Binary instance, this mean we got it
                 # from a query result and so it is already encrypted
@@ -203,7 +204,7 @@
                 value = self._cubicweb_to_datastore(attr, value)
             gaedict[attr] = value
         return gaedict
-    
+
     def to_gae_model(self):
         dbmodel = self._dbmodel
         dbmodel.update(self._to_gae_dict())
@@ -211,7 +212,7 @@
 
     @property
     @cached
-    def _dbmodel(self): 
+    def _dbmodel(self):
         if self.has_eid():
             assert self._gaeinitargs is None
             try:
@@ -240,7 +241,7 @@
             if '_app' in kwargs:
                 assert _app is None
                 _app = kwargs.pop('_app')
-            
+
             for key, value in kwargs.iteritems():
                 if key in self._attributes:
                     values['s_'+key] = value
@@ -267,10 +268,10 @@
         value will be prefixed by 'key_' to build the actual key name.
         """
         return None
-    
+
     def metainformation(self):
         return {'type': self.id, 'source': {'uri': 'system'}, 'extid': None}
-       
+
     def view(self, vid, __registry='views', **kwargs):
         """shortcut to apply a view on this entity"""
         return self.vreg.render(__registry, vid, self.req, rset=self.rset,
@@ -282,7 +283,7 @@
         if needcheck:
             return 'eid', False
         return mainattr, needcheck
-    
+
     def get_value(self, name):
         try:
             value = self[name]
@@ -306,7 +307,7 @@
             return True
         except BadKeyError:
             return False
-        
+
     def complete(self, skip_bytes=True):
         pass
 
@@ -319,7 +320,7 @@
             objs = Query(str(targettype)).Run()
         return rset_from_objs(self.req, objs, ('eid',),
                               'Any X WHERE X is %s' % targettype)
-    
+
     def key(self):
         return Key(self.eid)
 
@@ -334,7 +335,7 @@
                                        'Any X WHERE X eid %(x)s', {'x': self.eid})
             self.row = self.col = 0
         return dbmodel
-    
+
     @needrequest
     def get(cls, req, keys):
         # if check if this is a dict.key call
@@ -393,7 +394,7 @@
 
     def dynamic_properties(self):
         raise NotImplementedError('use eschema')
-        
+
     def is_saved(self):
         return self.has_eid()
 
@@ -425,7 +426,7 @@
 IntegerProperty = db.IntegerProperty
 FloatProperty = db.FloatProperty
 ListProperty = db.ListProperty
-SelfReferenceProperty = db.SelfReferenceProperty 
+SelfReferenceProperty = db.SelfReferenceProperty
 UserProperty = db.UserProperty
 
 
--- a/goa/dbinit.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/dbinit.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """some utility functions for datastore initialization.
 
 :organization: Logilab
-:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -72,7 +73,7 @@
         dsrelation = 'o_' + rschema.type
         if not dsrelation in gaeentity:
             gaeentity[dsrelation] = None
-    
+
 def fix_entities(schema):
     for etype in ('CWUser', 'CWGroup'):
         eschema = schema.eschema(etype)
@@ -81,7 +82,7 @@
             # XXX o_is on CWEType entity
             gaeentity['s_is'] = Key.from_path('CWEType', 'key_' + etype, parent=None)
             Put(gaeentity)
-    
+
 def init_persistent_schema(ssession, schema):
     execute = ssession.unsafe_execute
     rql = ('INSERT CWEType X: X name %(name)s, X description %(descr)s,'
@@ -105,4 +106,3 @@
         execute('INSERT CWProperty X: X pkey %(pk)s, X value%(v)s',
                 {'pk': u'system.version.%s' % cube,
                  'v': unicode(config.cube_version(cube))})
-    
--- a/goa/dbmyams.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/dbmyams.py	Wed Jun 03 09:15:20 2009 +0200
@@ -5,6 +5,10 @@
  - ReferenceProperty.verbose_name, collection_name, etc. (XXX)
 
 XXX proprify this knowing we'll use goa.db
+:organization: Logilab
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 from os.path import join
@@ -68,7 +72,7 @@
     # XXX no equivalent to Django's `auto_now`
     return dbm2y_default_factory(prop, **kwargs)
 
-    
+
 def dbm2y_relation_factory(etype, prop, multiple=False):
     """called if `prop` is a `db.ReferenceProperty`"""
     if multiple:
@@ -83,8 +87,8 @@
     except AttributeError, ex:
         # hack, data_type is still _SELF_REFERENCE_MARKER
         return SubjectRelation(etype, cardinality=cardinality)
-    
-    
+
+
 DBM2Y_FACTORY = {
     basestring: dbm2y_string_factory,
     datastore_types.Text: dbm2y_string_factory,
@@ -107,7 +111,7 @@
         self.created = []
         self.loaded_files = []
         self._instantiate_handlers()
-        
+
     def finalize(self, register_base_types=False):
         return self._build_schema('google-appengine', register_base_types)
 
@@ -148,11 +152,11 @@
     def import_yams_cube_schema(self, templpath):
         for filepath in self.get_schema_files(templpath):
             self.handle_file(filepath)
-        
+
     @property
     def pyreader(self):
         return self._live_handlers['.py']
-        
+
 import os
 from cubicweb import CW_SOFTWARE_ROOT
 
@@ -183,7 +187,7 @@
                      'is_', 'is_instance_of',
                      'read_permission', 'add_permission',
                      'delete_permission', 'update_permission'):
-        loader.import_yams_schema(erschema, 'bootstrap')  
+        loader.import_yams_schema(erschema, 'bootstrap')
     loader.handle_file(join(SCHEMAS_LIB_DIRECTORY, 'base.py'))
     cubes = config['included-yams-cubes']
     for cube in reversed(config.expand_cubes(cubes)):
@@ -211,4 +215,3 @@
         if getattr(ertype, 'inlined', False):
             ertype.inlined = False
     return loader.finalize()
-
--- a/goa/doc/devmanual_fr/chap_i18n.txt	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/doc/devmanual_fr/chap_i18n.txt	Wed Jun 03 09:15:20 2009 +0200
@@ -34,17 +34,17 @@
 Une fois l'application rendu internationalisable coté code, reste à gérer les
 catalogues de traductions. cubicweb-ctl intègre pour cela les commandes suivantes : 
 
-* `i18nlibupdate`, met à jour les catalogues de messages *de la librairie
+* `i18ncubicweb`, met à jour les catalogues de messages *de la librairie
   cubicweb*. Sauf si vous développez sur le framework (et non votre propre
   application), vous ne devriez pas avoir à utiliser cette commande
 
-* `i18nupdate`, met à jour les catalogues de messages *du composant* (ou de tous
+* `i18ncube`, met à jour les catalogues de messages *du composant* (ou de tous
   les composants). A la suite de cette commande, vous devez mettre à jour les
   fichiers de traduction *.po* dans le sous-répertoire "i18n" de votre
   template. Évidemment les traductions précédentes toujours utilisées ont été
   conservées.
 
-* `i18ncompile`, recompile les catalogues de messages *d'une instance* (ou de
+* `i18ninstance`, recompile les catalogues de messages *d'une instance* (ou de
   toutes les instances) après mise à jour des catalogues de son composant. Cela
   est effectué automatiquement lors d'une création ou d'une mise à jour. Les
   catalogues de messages compilés se trouvent dans le répertoire
@@ -58,9 +58,9 @@
 application (en ajoutant une nouvelle vue ou en ayant modifié le schéma par
 exemple) :
 
-1. `cubicweb-ctl i18nupdate <composant>`
+1. `cubicweb-ctl i18ncube <composant>`
 2. éditer les fichiers <composant>/xxx.po dans pour y rajouter les traductions
    manquantes (`msgstr` vide) 
 3. `hg ci -m "updated i18n catalogs"`
-4. `cubicweb-ctl i18n compile <monapplication>`
+4. `cubicweb-ctl i18ninstance <monapplication>`
 
--- a/goa/doc/devmanual_fr/sect_erudi-ctl.txt	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/doc/devmanual_fr/sect_erudi-ctl.txt	Wed Jun 03 09:15:20 2009 +0200
@@ -56,9 +56,9 @@
 
 Commandes pour la maintenance des catalogues i18n
 `````````````````````````````````````````````````
-* ``i18nlibupdate``, regénère les catalogues de messages de la librairie CubicWeb
-* ``i18nupdate``, regénère les catalogues de messages d'un composant
-* ``i18ncompile``, recompile les catalogues de messages d'une instance. Cela est
+* ``i18ncubicweb``, regénère les catalogues de messages de la librairie CubicWeb
+* ``i18ncube``, regénère les catalogues de messages d'un composant
+* ``i18ninstance``, recompile les catalogues de messages d'une instance. Cela est
   effectué automatiquement lors d'une upgrade
 
 Cf Internationalisation_.
--- a/goa/gaesource.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/gaesource.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Adapter for google appengine source.
 
 :organization: Logilab
-:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -15,7 +16,7 @@
 
 from google.appengine.api.datastore import Key, Entity, Put, Delete
 from google.appengine.api import datastore_errors, users
-    
+
 def _init_groups(guser, euser):
     # set default groups
     if guser is None:
@@ -82,13 +83,13 @@
             gaeentity[relation] = related or None
     _mark_modified(session, gaeentity)
 
-    
+
 class DatastorePutOp(SingleOperation):
     """delayed put of entities to have less datastore write api calls
 
     * save all modified entities at precommit (should be the first operation
       processed, hence the 0 returned by insert_index())
-      
+
     * in case others precommit operations modify some entities, resave modified
       entities at commit. This suppose that no db changes will occurs during
       commit event but it should be the case.
@@ -103,10 +104,10 @@
             assert not eid in pending
             Put(gaeentity)
         modified.clear()
-        
+
     def commit_event(self):
         self._put_entities()
-        
+
     def precommit_event(self):
         self._put_entities()
 
@@ -117,9 +118,9 @@
     passwd_rql = "Any P WHERE X is CWUser, X login %(login)s, X upassword P"
     auth_rql = "Any X WHERE X is CWUser, X login %(login)s, X upassword %(pwd)s"
     _sols = ({'X': 'CWUser', 'P': 'Password'},)
-    
+
     options = ()
-    
+
     def __init__(self, repo, appschema, source_config, *args, **kwargs):
         AbstractSource.__init__(self, repo, appschema, source_config,
                                 *args, **kwargs)
@@ -128,11 +129,11 @@
             self.authenticate = self.authenticate_gauth
         else:
             self.authenticate = self.authenticate_local
-            
+
     def reset_caches(self):
         """method called during test to reset potential source caches"""
         pass
-    
+
     def init_creating(self):
         pass
 
@@ -144,7 +145,7 @@
 
     def get_connection(self):
         return ConnectionWrapper()
-    
+
     # ISource interface #######################################################
 
     def compile_rql(self, rql):
@@ -152,7 +153,7 @@
         rqlst.restricted_vars = ()
         rqlst.children[0].solutions = self._sols
         return rqlst
-    
+
     def set_schema(self, schema):
         """set the application'schema"""
         self.interpreter = RQLInterpreter(schema)
@@ -161,13 +162,13 @@
             # rql syntax trees used to authenticate users
             self._passwd_rqlst = self.compile_rql(self.passwd_rql)
             self._auth_rqlst = self.compile_rql(self.auth_rql)
-                
+
     def support_entity(self, etype, write=False):
         """return true if the given entity's type is handled by this adapter
         if write is true, return true only if it's a RW support
         """
         return True
-    
+
     def support_relation(self, rtype, write=False):
         """return true if the given relation's type is handled by this adapter
         if write is true, return true only if it's a RW support
@@ -200,7 +201,7 @@
             _init_groups(guser, euser)
             Put(euser)
             return str(euser.key())
-        
+
     def authenticate_local(self, session, login, password):
         """return CWUser eid for the given login/password if this account is
         defined in this source, else raise `AuthenticationError`
@@ -224,8 +225,8 @@
             return rset[0][0]
         except IndexError:
             raise AuthenticationError('bad password')
-    
-    def syntax_tree_search(self, session, union, args=None, cachekey=None, 
+
+    def syntax_tree_search(self, session, union, args=None, cachekey=None,
                            varmap=None):
         """return result from this source for a rql query (actually from a rql
         syntax tree and a solution dictionary mapping each used variable to a
@@ -235,16 +236,16 @@
         results, description = self.interpreter.interpret(union, args,
                                                           session.datastore_get)
         return results # XXX description
-                
+
     def flying_insert(self, table, session, union, args=None, varmap=None):
         raise NotImplementedError
-    
+
     def add_entity(self, session, entity):
         """add a new entity to the source"""
         # do not delay add_entity as other modifications, new created entity
         # needs an eid
         entity.put()
-        
+
     def update_entity(self, session, entity):
         """replace an entity in the source"""
         gaeentity = entity.to_gae_model()
@@ -253,7 +254,7 @@
             for asession in self.repo._sessions.itervalues():
                 if asession.user.eid == entity.eid:
                     asession.user.update(dict(gaeentity))
-                
+
     def delete_entity(self, session, etype, eid):
         """delete an entity from the source"""
         # do not delay delete_entity as other modifications to ensure
@@ -270,7 +271,7 @@
         _radd(session, gaesubj, gaeobj.key(), 's_' + rtype, cards[0])
         _radd(session, gaeobj, gaesubj.key(), 'o_' + rtype, cards[1])
         _clear_related_cache(session, gaesubj, rtype, gaeobj)
-            
+
     def delete_relation(self, session, subject, rtype, object):
         """delete a relation from the source"""
         gaesubj, gaeobj, cards = _rinfo(session, subject, rtype, object)
@@ -280,7 +281,7 @@
         if not object in pending:
             _rdel(session, gaeobj, gaesubj.key(), 'o_' + rtype, cards[1])
         _clear_related_cache(session, gaesubj, rtype, gaeobj)
-        
+
     # system source interface #################################################
 
     def eid_type_source(self, session, eid):
@@ -290,7 +291,7 @@
         except datastore_errors.BadKeyError:
             raise UnknownEid(eid)
         return key.kind(), 'system', None
-    
+
     def create_eid(self, session):
         return None # let the datastore generating key
 
@@ -303,15 +304,14 @@
         record from the entities table to the deleted_entities table
         """
         pass
-        
+
     def fti_unindex_entity(self, session, eid):
         """remove text content for entity with the given eid from the full text
         index
         """
         pass
-        
+
     def fti_index_entity(self, session, entity):
         """add text content of a created/modified entity to the full text index
         """
         pass
-
--- a/goa/goaconfig.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/goaconfig.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """google appengine configuration
 
 :organization: Logilab
-:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -32,7 +33,7 @@
     """repository and web application in the same twisted process"""
     name = 'app'
     repo_method = 'inmemory'
-    options = merge_options(( 
+    options = merge_options((
         ('included-cubes',
          {'type' : 'csv',
           'default': [],
@@ -75,7 +76,7 @@
           'anonymous access using the app.yaml file)',
           'group': 'main', 'inputlevel': 1,
           }),
-        
+
         ) + WebConfiguration.options + ServerConfiguration.options)
     options = [(optname, optdict) for optname, optdict in options
                if not optname in UNSUPPORTED_OPTIONS]
@@ -94,10 +95,10 @@
     # deactivate some hooks during [pre|post]create scripts execution
     # (unique values check, owned_by/created_by relations setup)
     free_wheel = True
-    
+
     if not os.environ.get('APYCOT_ROOT'):
         CUBES_DIR = join(CW_SOFTWARE_ROOT, '../cubes')
-    
+
     def __init__(self, appid, apphome=None):
         if apphome is None:
             apphome = 'data'
@@ -111,7 +112,7 @@
         if key == 'base-url':
             return self._base_url
         return super(GAEConfiguration, self).__getitem__(key)
-    
+
     # overriden from cubicweb base configuration
 
     @property
@@ -136,15 +137,15 @@
 
     def instance_md5_version(self):
         return ''
-    
+
     def _init_base_url(self):
         pass
-    
+
     # overriden from cubicweb server configuration
-    
+
     def sources(self):
         return {'system': {'adapter': 'gae'}}
-    
+
     def load_schema(self, schemaclasses=None, extrahook=None):
         try:
             return self._schema
@@ -155,10 +156,11 @@
     # goa specific
     def repo_session(self, sessionid):
         return self.repository()._sessions[sessionid]
-    
+
     def is_anonymous_user(self, login):
         if self['use-google-auth']:
             from google.appengine.api import users
             return users.get_current_user() is None
         else:
             return login == self.anonymous_user()[0]
+
--- a/goa/goactl.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/goactl.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """cubicweb on appengine plugins for cubicweb-ctl
 
 :organization: Logilab
-:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -36,7 +37,7 @@
     (join(CW_SOFTWARE_ROOT, 'embedded', 'mx'), 'mx'),
     ('/usr/share/fckeditor/', 'fckeditor'),
 
-    (join(CW_SOFTWARE_ROOT, 'web', 'data'), join('cubes', 'shared', 'data')), 
+    (join(CW_SOFTWARE_ROOT, 'web', 'data'), join('cubes', 'shared', 'data')),
     (join(CW_SOFTWARE_ROOT, 'web', 'wdoc'), join('cubes', 'shared', 'wdoc')),
     (join(CW_SOFTWARE_ROOT, 'i18n'), join('cubes', 'shared', 'i18n')),
     (join(CW_SOFTWARE_ROOT, 'goa', 'tools'), 'tools'),
@@ -60,7 +61,7 @@
     'utils.py',
     'vregistry.py',
     'view.py',
-    
+
     'common/mail.py',
     'common/migration.py',
     'common/mixins.py',
@@ -93,12 +94,12 @@
 
     'sobjects/__init__.py',
     'sobjects/notification.py',
-    
+
 # XXX would be necessary for goa.testlib but require more stuff to be added
 #     such as server.serverconfig and so on (check devtools.__init__)
 #    'devtools/__init__.py',
 #    'devtools/fake.py',
-    
+
     'web/__init__.py',
     'web/_exceptions.py',
     'web/action.py',
@@ -139,7 +140,7 @@
     'wsgi/__init__.py',
     'wsgi/handler.py',
     'wsgi/request.py',
-    
+
     'goa/__init__.py',
     'goa/db.py',
     'goa/dbinit.py',
@@ -149,9 +150,9 @@
     'goa/gaesource.py',
     'goa/rqlinterpreter.py',
     'goa/appobjects/__init__.py',
-    'goa/appobjects/components.py', 
-    'goa/appobjects/dbmgmt.py', 
-    'goa/appobjects/gauthservice.py', 
+    'goa/appobjects/components.py',
+    'goa/appobjects/dbmgmt.py',
+    'goa/appobjects/gauthservice.py',
     'goa/appobjects/sessions.py',
 
     'schemas/bootstrap.py',
@@ -178,7 +179,7 @@
     """
     name = 'newgapp'
     arguments = '<application directory>'
-    
+
     def run(self, args):
         if len(args) != 1:
             raise BadCommandUsage("exactly one argument is expected")
@@ -196,7 +197,7 @@
                 create_dir(split(subdirectory)[0])
             create_symlink(directory, join(appldir, subdirectory))
         create_init_file(join(appldir, 'logilab'), 'logilab')
-        # copy supported part of cubicweb 
+        # copy supported part of cubicweb
         create_dir(join(appldir, 'cubicweb'))
         for fpath in COPY_CW_FILES:
             target = join(appldir, 'cubicweb', fpath)
--- a/goa/goavreg.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/goavreg.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """goa specific registry
 
 :organization: Logilab
-:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -19,7 +20,7 @@
     return 'cubes.%s.%s' % (cube, module)
 
 class GAERegistry(CubicWebRegistry):
-    
+
     def set_schema(self, schema):
         """disable reload hooks of cubicweb registry set_schema method"""
         self.schema = schema
@@ -37,7 +38,7 @@
         for cube in reversed(self.config.cubes()):
             self.load_cube(cube)
         self.load_application(applroot)
-        
+
     def load_directory(self, directory, cube, skip=()):
         for filename in listdir(directory):
             if filename[-3:] == '.py' and not filename in skip:
@@ -70,6 +71,3 @@
             # when using db.Model defined schema, the defined class is used as
             # entity class as well and so have to be registered
             self._import(_pkg_name(cube, 'schema'))
-
-
-    
--- a/goa/overrides/__init__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/overrides/__init__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,8 @@
 # server.__init__
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
--- a/goa/overrides/mttransforms.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/overrides/mttransforms.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """mime type transformation engine for cubicweb, based on mtconverter
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -38,7 +39,7 @@
 
 HAS_PIL_TRANSFORMS = False
 HAS_PYGMENTS_TRANSFORMS = False
-    
+
 class html_to_text(Transform):
     inputs = HTML_MIMETYPES
     output = 'text/plain'
--- a/goa/overrides/rqlannotation.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/overrides/rqlannotation.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,6 +1,14 @@
+"""
+:organization: Logilab
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
+__docformat__ = "restructuredtext en"
+
 def set_qdata(getrschema, union, noinvariant):
     pass
-        
+
 class SQLGenAnnotator(object):
     def __init__(self, schema):
         self.schema = schema
@@ -9,5 +17,5 @@
     def annotate(self, rqlst):
         rqlst.has_text_query = False
         rqlst.need_distinct = False
-        
-   
+
+
--- a/goa/overrides/server__init__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/overrides/server__init__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,11 @@
 # server debugging flag
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 DEBUG = False
 
 # sqlite'stored procedures have to be registered at connexion opening time
--- a/goa/overrides/server_utils.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/overrides/server_utils.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 class RepoThread(object):
     def __init__(self, *args):
@@ -6,7 +13,7 @@
         pass
     def join(self):
         pass
-    
+
 class LoopTask(RepoThread):
     def cancel(self):
         pass
--- a/goa/overrides/toolsutils.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/overrides/toolsutils.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 import sys
 from cubicweb import warning
 
--- a/goa/rqlinterpreter.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/rqlinterpreter.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """provide a minimal RQL support for google appengine dbmodel
 
 :organization: Logilab
-:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -23,7 +24,7 @@
     return Key(key).kind()
 
 def poss_var_types(myvar, ovar, kind, solutions):
-    return frozenset(etypes[myvar] for etypes in solutions 
+    return frozenset(etypes[myvar] for etypes in solutions
                      if etypes[ovar] == kind)
 
 def expand_result(results, result, myvar, values, dsget=None):
@@ -84,7 +85,7 @@
             string.append('%s: %s' % (k, v))
     return '{%s}' % ', '.join(string)
 
-                         
+
 class EidMismatch(Exception):
     def __init__(self, varname, value):
         self.varname = varname
@@ -101,45 +102,45 @@
         self.operator = operator
         self.rtype = rel.r_type
         self.var = rel.children[0]
-        
+
     def __repr__(self):
         return '<%s for %s>' % (self.__class__.__name__, self.rel)
-    
+
     @property
     def rhs(self):
         return self.rel.children[1].children[0]
 
-        
+
 class MultipleRestriction(object):
     def __init__(self, restrictions):
         self.restrictions = restrictions
-        
+
     def resolve(self, solutions, fixed):
         return _resolve(self.restrictions, solutions, fixed)
 
-    
+
 class VariableSelection(Restriction):
     def __init__(self, rel, dsget, prefix='s'):
         Restriction.__init__(self, rel)
         self._dsget = dsget
         self._not = self.rel.neged(strict=True)
         self._prefix = prefix + '_'
-        
+
     def __repr__(self):
         return '<%s%s for %s>' % (self._prefix[0], self.__class__.__name__, self.rel)
-        
+
     @property
     def searched_var(self):
         if self._prefix == 's_':
             return self.var.name
         return self.rhs.name
-        
+
     @property
     def constraint_var(self):
         if self._prefix == 's_':
             return self.rhs.name
         return self.var.name
-        
+
     def _possible_values(self, myvar, ovar, entity, solutions, dsprefix):
         if self.rtype == 'identity':
             return (entity.key(),)
@@ -150,7 +151,7 @@
             value = [value]
         vartypes = poss_var_types(myvar, ovar, entity.kind(), solutions)
         return (v for v in value if v.kind() in vartypes)
-        
+
     def complete_and_filter(self, solutions, results):
         myvar = self.rhs.name
         ovar = self.var.name
@@ -173,8 +174,8 @@
                     expand_result(results, result, myvar, values, self._dsget)
         else:
             assert self.rhs.name in results[0]
-            self.object_complete_and_filter(solutions, results)           
-            
+            self.object_complete_and_filter(solutions, results)
+
     def filter(self, solutions, results):
         myvar = self.rhs.name
         ovar = self.var.name
@@ -187,10 +188,10 @@
                 newsols[key] = frozenset(v for v in values)
             if self._not:
                 if result[myvar].key() in newsols[key]:
-                    results.remove(result)                
+                    results.remove(result)
             elif not result[myvar].key() in newsols[key]:
                 results.remove(result)
-    
+
     def object_complete_and_filter(self, solutions, results):
         if self._not:
             raise NotImplementedError()
@@ -201,7 +202,7 @@
                                            solutions, 'o_')
             expand_result(results, result, myvar, values, self._dsget)
 
-    
+
 class EidRestriction(Restriction):
     def __init__(self, rel, dsget):
         Restriction.__init__(self, rel)
@@ -216,7 +217,7 @@
 
     def _get_value(self, fixed):
         return fixed[self.constraint_var].key()
-    
+
     def fill_query(self, fixed, query, operator=None):
         restr = '%s%s %s' % (self._prefix, self.rtype, operator or self.operator)
         query[restr] = self._get_value(fixed)
@@ -235,7 +236,7 @@
 
     def _get_value(self, fixed):
         return None
-    
+
     def resolve(self, solutions, fixed):
         if self.rtype == 'identity':
             raise NotImplementedError()
@@ -255,7 +256,7 @@
                 raise NotImplementedError('LIKE is only supported for prefix search')
             self.operator = '>'
             self.value = value[:-1]
-            
+
     def complete_and_filter(self, solutions, results):
         # check lhs var first in case this is a restriction
         assert self._not
@@ -263,7 +264,7 @@
         for result in results[:]:
             if result[myvar].get('s_'+rtype) == value:
                 results.remove(result)
-            
+
     def _get_value(self, fixed):
         return self.value
 
@@ -294,7 +295,7 @@
     @property
     def operator(self):
         return 'in'
-            
+
 
 class TypeRestriction(AttributeRestriction):
     def __init__(self, var):
@@ -302,7 +303,7 @@
 
     def __repr__(self):
         return '<%s for %s>' % (self.__class__.__name__, self.var)
-    
+
     def resolve(self, solutions, fixed):
         objs = []
         for etype in frozenset(etypes[self.var.name] for etypes in solutions):
@@ -330,7 +331,7 @@
         self.args = args
         self.term = term
         self._solution = self.term.stmt.solutions[0]
-        
+
     def compute(self, result):
         """return (entity type, value) to which self.term is evaluated according
         to the given result dictionnary and to query arguments (self.args)
@@ -341,7 +342,7 @@
         args = tuple(n.accept(self, result)[1] for n in node.children)
         value = self.functions[node.name](*args)
         return node.get_type(self._solution, self.args), value
-    
+
     def visit_variableref(self, node, result):
         value = result[node.name]
         try:
@@ -350,11 +351,11 @@
         except AttributeError:
             etype = self._solution[node.name]
         return etype, value
-    
+
     def visit_constant(self, node, result):
         return node.get_type(kwargs=self.args), node.eval(self.args)
-    
-        
+
+
 class RQLInterpreter(object):
     """algorithm:
     1. visit the restriction clauses and collect restriction for each subject
@@ -369,7 +370,7 @@
            for each solution in select'solutions:
                1. resolve variables which have attribute restriction
                2. resolve relation restriction
-               3. resolve selection and add to global results 
+               3. resolve selection and add to global results
     """
     def __init__(self, schema):
         self.schema = schema
@@ -379,15 +380,15 @@
                              'UPPER': lambda x: x.upper()}
         for cb in SQL_CONNECT_HOOKS.get('sqlite', []):
             cb(self)
-            
+
     # emulate sqlite connection interface so we can reuse stored procedures
     def create_function(self, name, nbargs, func):
         self._stored_proc[name] = func
-        
+
     def create_aggregate(self, name, nbargs, func):
         self._stored_proc[name] = func
 
-        
+
     def execute(self, operation, parameters=None, eid_key=None, build_descr=True):
         rqlst = self.rqlhelper.parse(operation, annotate=True)
         try:
@@ -397,7 +398,7 @@
         else:
             results, description = self.interpret(rqlst, parameters)
         return ResultSet(results, operation, parameters, description, rqlst=rqlst)
-        
+
     def interpret(self, node, kwargs, dsget=None):
         if dsget is None:
             self._dsget = Get
@@ -417,7 +418,7 @@
             results += pres
             description += pdescr
         return results, description
-    
+
     def visit_select(self, node, extra):
         constraints = {}
         if node.where is not None:
@@ -441,7 +442,7 @@
         for varname, restrictions in constraints.iteritems():
             for restr in restrictions[:]:
                 if isinstance(restr, EidRestriction):
-                    assert not varname in fixed    
+                    assert not varname in fixed
                     try:
                         value = restr.resolve(kwargs)
                         fixed[varname] = value
@@ -455,7 +456,7 @@
                 if isinstance(restr, AttributeRestriction):
                     toresolve.setdefault(varname, []).append(restr)
                 elif isinstance(restr, NotRelationRestriction) or (
-                    isinstance(restr, RelationRestriction) and 
+                    isinstance(restr, RelationRestriction) and
                     not restr.searched_var in fixed and restr.constraint_var in fixed):
                     toresolve.setdefault(varname, []).append(restr)
                 else:
@@ -495,7 +496,7 @@
                     partres.append({varname: value})
             elif not varname in partres[0]:
                 # cartesian product
-                for res in partres:                    
+                for res in partres:
                     res[varname] = values[0]
                 for res in partres[:]:
                     for value in values[1:]:
@@ -503,14 +504,14 @@
                         res[varname] = value
                         partres.append(res)
             else:
-                # union 
+                # union
                 for res in varpartres:
                     for value in values:
                         res = res.copy()
                         res[varname] = value
                         partres.append(res)
         #print 'partres', len(partres)
-        #print partres                        
+        #print partres
         # Note: don't check for empty partres since constant selection may still
         # produce result at this point
         # sort to get RelationRestriction before AttributeSelection
@@ -569,14 +570,14 @@
                     append_result(res, descr, i, j, value, etype)
         #print '--------->', res
         return res, descr
-    
-    def visit_and(self, node, constraints, extra): 
+
+    def visit_and(self, node, constraints, extra):
         for child in node.children:
             child.accept(self, constraints, extra)
     def visit_exists(self, node, constraints, extra):
         extra['has_exists'] = True
         self.visit_and(node, constraints, extra)
-    
+
     def visit_not(self, node, constraints, extra):
         for child in node.children:
             child.accept(self, constraints, extra)
@@ -584,7 +585,7 @@
             extra.pop(node)
         except KeyError:
             raise NotImplementedError()
-        
+
     def visit_relation(self, node, constraints, extra):
         if node.is_types_restriction():
             return
@@ -600,7 +601,7 @@
             self._visit_non_final_neged_relation(rschema, node, constraints)
         else:
             self._visit_non_final_relation(rschema, node, constraints)
-                
+
     def _visit_non_final_relation(self, rschema, node, constraints, not_=False):
         lhs, rhs = node.get_variable_parts()
         for v1, v2, prefix in ((lhs, rhs, 's'), (rhs, lhs, 'o')):
@@ -611,14 +612,14 @@
             if nbrels > 1:
                 constraints.setdefault(v1.name, []).append(
                     RelationRestriction(node, self._dsget, prefix))
-                # just init an empty list for v2 variable to avoid a 
+                # just init an empty list for v2 variable to avoid a
                 # TypeRestriction being added for it
                 constraints.setdefault(v2.name, [])
                 break
         else:
             constraints.setdefault(rhs.name, []).append(
                 VariableSelection(node, self._dsget, 's'))
-                
+
     def _visit_non_final_neged_relation(self, rschema, node, constraints):
         lhs, rhs = node.get_variable_parts()
         for v1, v2, prefix in ((lhs, rhs, 's'), (rhs, lhs, 'o')):
@@ -653,16 +654,16 @@
                     AttributeInRestriction(node, extra['kwargs']))
             else:
                 raise NotImplementedError()
-        
+
     def _not_implemented(self, *args, **kwargs):
         raise NotImplementedError()
-    
+
     visit_or = _not_implemented
     # shouldn't occurs
     visit_set = _not_implemented
     visit_insert = _not_implemented
     visit_delete = _not_implemented
-        
+
 
 from logging import getLogger
 from cubicweb import set_log_methods
--- a/goa/skel/custom.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/skel/custom.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 def postinit(vreg):
     """this callback is called at the end of initialization process
     and can be used to load explicit modules (views or entities).
--- a/goa/skel/loader.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/skel/loader.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,10 +1,17 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 if __name__ == '__main__':
 
     from os.path import dirname, abspath
     from cubicweb import goa
     from cubicweb.goa.goaconfig import GAEConfiguration
     from cubicweb.goa.dbinit import create_user, create_groups
-    
+
     # compute application's root directory
     APPLROOT = dirname(abspath(__file__))
     # apply monkey patches first
--- a/goa/skel/main.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/skel/main.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 to change anything here.
 
 :organization: Logilab
-:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/goa/skel/schema.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/skel/schema.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from cubicweb.schema import format_constraint
 
 class Blog(EntityType):
--- a/goa/skel/views.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/skel/views.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,11 @@
 # custom application views
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from datetime import date
 
 from cubicweb.utils import last_day
--- a/goa/test/data/__init__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/test/data/__init__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,7 @@
-"""zou"""
+"""zou
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
--- a/goa/test/data/schema.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/test/data/schema.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 
 class YamsEntity(EntityType):
--- a/goa/test/data/settings.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/test/data/settings.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,8 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 TEMPLATE_DEBUG = False
--- a/goa/test/data/views.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/test/data/views.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,11 @@
 # -*- coding: utf-8 -*-
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 import os
 os.environ["DJANGO_SETTINGS_MODULE"] = 'data.settings'
 
@@ -21,7 +28,7 @@
 
 class MyIndex(StartupView):
     id = 'index'
-    
+
     def call(self):
         ctx = template.Context({'user': self.req.user})
         return INDEX_TEMPLATE.render(ctx)
--- a/goa/test/pytestconf.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/test/pytestconf.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,5 @@
 """this pytestconf automatically adds the mx's python version in the PYTHONPATH
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 import sys
 import os.path as osp
--- a/goa/test/unittest_db.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/test/unittest_db.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,11 @@
 # -*- coding: utf-8 -*-
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from cubicweb.goa.testlib import *
 
 from cubicweb import Binary
@@ -10,11 +17,11 @@
 
 class Blog(db.Model):
     data = db.BlobProperty()
-    
+
 class DBTest(GAEBasedTC):
     config = GAEConfiguration('toto')
     config.global_set_option('use-google-auth', False)
-    
+
     MODEL_CLASSES = (Blog,)
 
     def test_set_none_relation(self):
@@ -25,7 +32,7 @@
     def test_euser_key(self):
         euser = self.add_entity('CWUser', login=u'toto', upassword='toto')
         self.assertEquals(euser.key().name(), 'key_toto')
-        
+
     def test_egroup_key(self):
         egroup = self.add_entity('CWGroup', name=u'toto')
         self.assertEquals(egroup.key().name(), 'key_toto')
@@ -40,7 +47,7 @@
         text = u'e'*501
         entity = self.add_entity('State', name=u'test', description=text)
         self.assertIsInstance(entity.description, unicode)
-        self.failIf(isinstance(entity.description, Text)) 
+        self.failIf(isinstance(entity.description, Text))
         self.assertEquals(entity.description, text)
 
     def test_long_accentued_text(self):
@@ -48,7 +55,7 @@
         text = u'é'*500
         entity = self.add_entity('State', name=u'test', description=text)
         self.assertIsInstance(entity.description, unicode)
-        self.failIf(isinstance(entity.description, Text)) 
+        self.failIf(isinstance(entity.description, Text))
         self.assertEquals(entity.description, text)
 
     def test_blob(self):
@@ -56,10 +63,10 @@
         entity = self.add_entity('Blog', data=data)
         self.assertIsInstance(entity.data, Binary)
         value = entity.data.getvalue()
-        self.failIf(isinstance(value, Blob)) 
+        self.failIf(isinstance(value, Blob))
         self.assertEquals(value, data)
-        
-        
+
+
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main
     unittest_main()
--- a/goa/test/unittest_editcontroller.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/test/unittest_editcontroller.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from cubicweb.goa.testlib import *
 
 from urllib import unquote
@@ -12,23 +19,23 @@
 
 
 class EditControllerTC(GAEBasedTC):
-    
+
     config = GAEConfiguration('toto')
     config.global_set_option('use-google-auth', False)
     config.global_set_option('schema-type', 'yams')
     config.global_set_option('included-cubes', ())
     config.global_set_option('included-yams-cubes', ('blog',))
-    
+
     MODEL_CLASSES = ()
     from cubicweb.web.views import editcontroller
     from cubicweb.entities import lib
     LOAD_APP_MODULES = (editcontroller, lib)
-    
+
     def setUp(self):
         GAEBasedTC.setUp(self)
         self.req = self.request()
         self.ctrl = self.get_ctrl(self.req)
-        
+
     def get_ctrl(self, req):
         return self.vreg.select(self.vreg.registry_objects('controllers', 'edit'),
                                 req=req, appli=self)
@@ -69,13 +76,13 @@
         """check behaviour of this controller without any form parameter"""
         self.req.form = {}
         self.assertRaises(ValidationError, self.publish, self.req)
-        
+
     def test_validation_unique(self):
-        """test creation of two linked entities"""        
+        """test creation of two linked entities"""
         user = self.user
         self.req.form = {'eid': 'X', '__type:X': 'CWUser',
-                         'login:X': self.user.login, 'edits-login:X': u'', 
-                         'upassword:X': u'toto', 'upassword-confirm:X': u'toto', 'edits-upassword:X': u'', 
+                         'login:X': self.user.login, 'edits-login:X': u'',
+                         'upassword:X': u'toto', 'upassword-confirm:X': u'toto', 'edits-upassword:X': u'',
                          }
         self.assertRaises(ValidationError, self.publish, self.req)
 
@@ -155,23 +162,23 @@
         self.assertUnorderedIterableEquals([g.eid for g in e.in_group], groupeids)
         #stateeids = [eid for eid, in self.req.execute('State S WHERE S name "activated"')]
         #self.assertEquals([s.eid for s in e.in_state], stateeids)
-        
-        
+
+
     def test_create_multiple_linked(self):
         gueid = self.req.execute('CWGroup G WHERE G name "users"')[0][0]
         self.req.form = {'eid': ['X', 'Y'],
-                         
+
                          '__type:X': 'CWUser',
                          '__maineid' : 'X',
-                         'login:X': u'adim', 'edits-login:X': u'', 
-                         'upassword:X': u'toto', 'upassword-confirm:X': u'toto', 'edits-upassword:X': u'', 
+                         'login:X': u'adim', 'edits-login:X': u'',
+                         'upassword:X': u'toto', 'upassword-confirm:X': u'toto', 'edits-upassword:X': u'',
                          'surname:X': u'Di Mascio', 'edits-surname:X': '',
 
-                         'in_group:X': gueid, 'edits-in_group:X': INTERNAL_FIELD_VALUE, 
-                         
+                         'in_group:X': gueid, 'edits-in_group:X': INTERNAL_FIELD_VALUE,
+
                          '__type:Y': 'EmailAddress',
                          'address:Y': u'dima@logilab.fr', 'edits-address:Y': '',
-                         'use_email:X': 'Y', 'edits-use_email:X': INTERNAL_FIELD_VALUE, 
+                         'use_email:X': 'Y', 'edits-use_email:X': INTERNAL_FIELD_VALUE,
                          }
         path, params = self.expect_redirect_publish()
         # should be redirected on the created person
@@ -180,17 +187,17 @@
         self.assertEquals(e.surname, 'Di Mascio')
         email = e.use_email[0]
         self.assertEquals(email.address, 'dima@logilab.fr')
-        
+
     def test_edit_multiple_linked(self):
         peid = self.create_user('adim').eid
         self.req.form = {'eid': [peid, 'Y'],
                          '__type:%s'%peid: 'CWUser',
                          'surname:%s'%peid: u'Di Masci', 'edits-surname:%s'%peid: '',
-                         
+
                          '__type:Y': 'EmailAddress',
                          'address:Y': u'dima@logilab.fr', 'edits-address:Y': '',
                          'use_email:%s'%peid: 'Y', 'edits-use_email:%s'%peid: INTERNAL_FIELD_VALUE,
-                         
+
                          '__redirectrql': 'Any X WHERE X eid %s'%peid,
                          }
         path, params = self.expect_redirect_publish()
@@ -200,14 +207,14 @@
         self.assertEquals(e.surname, 'Di Masci')
         email = e.use_email[0]
         self.assertEquals(email.address, 'dima@logilab.fr')
-        
+
         emaileid = email.eid
         self.req.form = {'eid': [peid, emaileid],
                          '__type:%s'%peid: 'CWUser',
                          'surname:%s'%peid: u'Di Masci', 'edits-surname:%s'%peid: 'Di Masci',
                          '__type:%s'%emaileid: 'EmailAddress',
                          'address:%s'%emaileid: u'adim@logilab.fr', 'edits-address:%s'%emaileid: 'dima@logilab.fr',
-                         'use_email:%s'%peid: emaileid, 'edits-use_email:%s'%peid: emaileid, 
+                         'use_email:%s'%peid: emaileid, 'edits-use_email:%s'%peid: emaileid,
                          '__redirectrql': 'Any X WHERE X eid %s'%peid,
                          }
         path, params = self.expect_redirect_publish()
@@ -220,21 +227,21 @@
         email = e.use_email[0]
         self.assertEquals(email.address, 'adim@logilab.fr')
 
-        
+
     def test_password_confirm(self):
         """test creation of two linked entities
-        """        
+        """
         user = self.user
         self.req.form = {'__cloned_eid:X': user.eid,
                          'eid': 'X', '__type:X': 'CWUser',
-                         'login:X': u'toto', 'edits-login:X': u'', 
-                         'upassword:X': u'toto', 'edits-upassword:X': u'', 
+                         'login:X': u'toto', 'edits-login:X': u'',
+                         'upassword:X': u'toto', 'edits-upassword:X': u'',
                          }
         self.assertRaises(ValidationError, self.publish, self.req)
         self.req.form = {'__cloned_eid:X': user.eid,
                          'eid': 'X', '__type:X': 'CWUser',
-                         'login:X': u'toto', 'edits-login:X': u'', 
-                         'upassword:X': u'toto', 'upassword-confirm:X': u'tutu', 'edits-upassword:X': u'', 
+                         'login:X': u'toto', 'edits-login:X': u'',
+                         'upassword:X': u'toto', 'upassword-confirm:X': u'tutu', 'edits-upassword:X': u'',
                          }
         self.assertRaises(ValidationError, self.publish, self.req)
 
@@ -288,7 +295,7 @@
             self.assertEquals(rset[0][0], 'FOO')
         finally:
             del CWUser.custom_login_edit
-        
+
     def test_redirect_apply_button(self):
         redirectrql = rql_for_eid(4012) # whatever
         self.req.form = {
@@ -355,21 +362,21 @@
         path, params = self.expect_redirect_publish()
         self.assertEquals(path, 'view')
         self.assertEquals(params, {u'__message': u'entities deleted'})
-        
+
 
     def test_nonregr_multiple_empty_email_addr(self):
         gueid = self.req.execute('CWGroup G WHERE G name "users"')[0][0]
         self.req.form = {'eid': ['X', 'Y'],
-                         
+
                          '__type:X': 'CWUser',
-                         'login:X': u'adim', 'edits-login:X': u'', 
-                         'upassword:X': u'toto', 'upassword-confirm:X': u'toto', 'edits-upassword:X': u'', 
-                         'in_group:X': gueid, 'edits-in_group:X': INTERNAL_FIELD_VALUE, 
-                         
+                         'login:X': u'adim', 'edits-login:X': u'',
+                         'upassword:X': u'toto', 'upassword-confirm:X': u'toto', 'edits-upassword:X': u'',
+                         'in_group:X': gueid, 'edits-in_group:X': INTERNAL_FIELD_VALUE,
+
                          '__type:Y': 'EmailAddress',
                          'address:Y': u'', 'edits-address:Y': '',
                          'alias:Y': u'', 'edits-alias:Y': '',
-                         'use_email:X': 'Y', 'edits-use_email:X': INTERNAL_FIELD_VALUE, 
+                         'use_email:X': 'Y', 'edits-use_email:X': INTERNAL_FIELD_VALUE,
                          }
         self.assertRaises(ValidationError, self.publish, self.req)
 
@@ -386,7 +393,7 @@
                          {'p' : p.eid, 'e' : e.eid})
             self.req.form = {'__cloned_eid:X': p.eid,
                              'eid': 'X', '__type:X': 'CWUser',
-                             'login': u'dodo', 'edits-login': u'dodo', 
+                             'login': u'dodo', 'edits-login': u'dodo',
                              'surname:X': u'Boom', 'edits-surname:X': u'',
                              '__errorurl' : "whatever but required",
                              }
@@ -405,7 +412,7 @@
         finally:
             p.__class__.skip_copy_for = old_skips
 
-        
+
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main
     unittest_main()
--- a/goa/test/unittest_metadata.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/test/unittest_metadata.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from cubicweb.goa.testlib import *
 
 import time
@@ -7,7 +14,7 @@
 
 from google.appengine.api import datastore
 
-class Article(db.Model):        
+class Article(db.Model):
     content = db.TextProperty()
     synopsis = db.StringProperty(default='hello')
 
@@ -15,20 +22,20 @@
     diem = db.DateProperty(required=True, auto_now_add=True)
     title = db.StringProperty(required=True)
     content = db.TextProperty()
-    talks_about = db.ReferenceProperty(Article) 
-    cites = db.SelfReferenceProperty() 
+    talks_about = db.ReferenceProperty(Article)
+    cites = db.SelfReferenceProperty()
 
-  
+
 class MetaDataTC(GAEBasedTC):
     MODEL_CLASSES = (Article, Blog)
-    
+
     def setUp(self):
         GAEBasedTC.setUp(self)
         self.req = self.request()
         self.a = self.add_entity('Article')
         self.p = self.add_entity('CWProperty', pkey=u'ui.language', value=u'en')
         self.session.commit()
-        
+
     def _test_timestamp(self, entity, attr, sleep=0.1):
         timestamp = getattr(entity, attr)
         self.failUnless(timestamp)
@@ -41,20 +48,20 @@
             entity.set_attributes(value=u'en')
         self.session.commit()
         return timestamp
-    
+
     def test_creation_date_dbmodel(self):
         cdate = self._test_timestamp(self.a, 'creation_date')
         self.assertEquals(cdate, self.a.creation_date)
-        
+
     def test_creation_date_yams(self):
         cdate = self._test_timestamp(self.p, 'creation_date')
         self.assertEquals(cdate, self.p.creation_date)
-        
+
     def test_modification_date_dbmodel(self):
         mdate = self._test_timestamp(self.a, 'modification_date', sleep=1)
         a = self.execute('Any X WHERE X eid %(x)s', {'x': self.a.eid}, 'x').get_entity(0, 0)
         self.failUnless(mdate < a.modification_date, (mdate, a.modification_date))
-        
+
     def test_modification_date_yams(self):
         mdate = self._test_timestamp(self.p, 'modification_date', sleep=1)
         p = self.execute('Any X WHERE X eid %(x)s', {'x': self.p.eid}, 'x').get_entity(0, 0)
@@ -67,10 +74,10 @@
         dbmodel = entity.to_gae_model()
         self.assertEquals(len(dbmodel['s_owned_by']), 1)
         self.assertIsInstance(dbmodel['s_owned_by'][0], datastore.Key)
-        
+
     def test_owned_by_dbmodel(self):
         self._test_owned_by(self.a)
-        
+
     def test_owned_by_yams(self):
         self._test_owned_by(self.p)
 
@@ -79,16 +86,16 @@
         creator = entity.created_by[0]
         self.assertIsInstance(creator, db.Model)
         self.assertIsInstance(entity.to_gae_model()['s_created_by'], datastore.Key)
-        
+
     def test_created_by_dbmodel(self):
         self._test_created_by(self.a)
-        
+
     def test_created_by_dbmodel(self):
         self._test_created_by(self.p)
-        
+
     def test_user_owns_dbmodel(self):
         self.failUnless(self.req.user.owns(self.a.eid))
-        
+
     def test_user_owns_yams(self):
         self.failUnless(self.req.user.owns(self.p.eid))
 
@@ -96,11 +103,11 @@
         en = self.execute('Any EN WHERE E name EN, X is E, X eid %(x)s', {'x': self.a.eid}, 'x')[0][0]
         self.assertEquals(en, 'Article')
         en = self.execute('Any EN WHERE E name EN, X is E, X eid %(x)s', {'x': self.p.eid}, 'x')[0][0]
-        self.assertEquals(en, 'CWProperty') 
+        self.assertEquals(en, 'CWProperty')
         en = self.execute('Any EN WHERE E name EN, X is E, X eid %(x)s', {'x': self.req.user.eid}, 'x')[0][0]
         self.assertEquals(en, 'CWUser')
 
-        
+
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main
     unittest_main()
--- a/goa/test/unittest_rql.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/test/unittest_rql.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from cubicweb.goa.testlib import *
 
 from cubicweb import Binary
@@ -32,7 +39,7 @@
 
 # end stored procedure definition #############################################
 
-class Article(db.Model):        
+class Article(db.Model):
     content = db.TextProperty()
     synopsis = db.StringProperty(default=u'hello')
 
@@ -40,14 +47,14 @@
     diem = db.DateProperty(required=True, auto_now_add=True)
     content = db.TextProperty()
     itemtype = db.StringProperty(required=True, choices=(u'personal', u'business'))
-    talks_about = db.ReferenceProperty(Article) 
-    cites = db.SelfReferenceProperty() 
+    talks_about = db.ReferenceProperty(Article)
+    cites = db.SelfReferenceProperty()
     data = db.BlobProperty()
 
-    
+
 class RQLTest(GAEBasedTC):
     MODEL_CLASSES = (Article, Blog)
-    
+
     def setUp(self):
         GAEBasedTC.setUp(self)
         # hack to make talks_about cardinality to ** instead of ?*
@@ -59,13 +66,13 @@
         self.execute('SET X talks_about Y WHERE X eid %(x)s, Y eid %(y)s',
                      {'x': self.blog.eid, 'y': self.article.eid})
         self.commit()
-        
+
     def _check_rset_size(self, rset, row, col):
         self.assertEquals(len(rset), row)
         self.assertEquals(len(rset[0]), col)
         self.assertEquals(len(rset.description), row)
         self.assertEquals(len(rset.description[0]), col)
-        
+
     def _check_blog_rset(self, rset):
         self._check_rset_size(rset, 1, 1)
         self.assertEquals(rset.description[0][0], 'Blog')
@@ -121,20 +128,20 @@
         self.assertEquals(len(rset), 2)
         self.assertEquals(rset.description, [('Blog',), ('Blog',)])
 
-        
+
     def test_2_attribute_selection_1(self):
         rset = self.req.execute('Any X,D,C WHERE X is Blog, X diem D, X content C')
         self._check_rset_size(rset, 1, 3)
         self.assertEquals(rset[0], [self.blog.eid, today(), u'hop'])
         self.assertEquals(rset.description[0], ('Blog', 'Date', 'String'))
         self.assertIsInstance(rset[0][1], DateTimeType)
-        
+
     def test_2_attribute_selection_2(self):
         rset = self.req.execute('Any D,C WHERE X is Blog, X diem D, X content C')
         self._check_rset_size(rset, 1, 2)
         self.assertEquals(rset[0], [today(), u'hop'])
         self.assertEquals(rset.description[0], ('Date', 'String'))
-        
+
     def test_2_attribute_selection_binary(self):
         rset = self.req.execute('Any D WHERE X is Blog, X data D')
         self._check_rset_size(rset, 1, 1)
@@ -147,19 +154,19 @@
         self.assertIsInstance(rset[0][0], Binary)
         value = rset[0][0].getvalue()
         self.assertIsInstance(value, str)
-        self.failIf(isinstance(value, Blob)) 
+        self.failIf(isinstance(value, Blob))
         self.assertEquals(value, 'raw data')
         self.assertEquals(rset.description[0], ('Bytes',))
-        
+
     def test_2_attribute_selection_long_text(self):
         self.blog['content'] = text = 'a'*501
         self.blog.put()
         rset = self.req.execute('Any C WHERE X is Blog, X content C')
         self._check_rset_size(rset, 1, 1)
         self.assertIsInstance(rset[0][0], unicode)
-        self.failIf(isinstance(rset[0][0], Text)) 
+        self.failIf(isinstance(rset[0][0], Text))
         self.assertEquals(rset[0][0], text)
-        
+
     def test_2_attribute_selection_transformation(self):
         rset = self.req.execute('Any X,UPPER(C) WHERE X is Blog, X content C')
         self._check_rset_size(rset, 1, 2)
@@ -172,15 +179,15 @@
         self._check_blog_rset(rset)
         rset = self.req.execute('Any X WHERE X itemtype "business"')
         self.assertEquals(len(rset), 0)
-        
+
     def test_3_ambigous_attribute_restriction_1(self):
         rset = self.req.execute('Any X WHERE X content "hello"')
         self.assertEquals(len(rset), 0)
-        
+
     def test_3_ambigous_attribute_restriction_2(self):
         rset = self.req.execute('Any X WHERE X content "hop"')
         self._check_blog_rset(rset)
-        
+
     def test_3_ambigous_attribute_restriction_3(self):
         article = Article(content=u'hop')
         article.put()
@@ -193,11 +200,11 @@
         rset = self.req.execute('Any X WHERE X eid %(x)s, X content "hola"',
                                 {'x': self.blog.eid})
         self.assertEquals(len(rset), 0)
-        
+
     def test_3_multiple_attribute_restriction(self):
         rset = self.req.execute('Any X WHERE X content "hop", X itemtype "personal"')
         self._check_blog_rset(rset)
-        
+
     def test_3_incoherant_multiple_attribute_restriction(self):
         rset = self.req.execute('Any X WHERE X content "hip", X itemtype "personal"')
         self.assertEquals(len(rset), 0)
@@ -234,7 +241,7 @@
         repo = self.config.repository()
         versions = repo.get_versions()
         self.assertEquals(versions.keys(), ['cubicweb'])
-    
+
     def _setup_relation_description(self):
         self.article2 = self.add_entity('Article', content=u'hop')
         self.blog2 = self.add_entity('Blog', itemtype=u'personal', content=u'hip')
@@ -242,7 +249,7 @@
                      {'x': self.blog2.eid, 'y': self.article2.eid})
         self.blog3 = self.add_entity('Blog', itemtype=u'business', content=u'hep')
         self.commit()
-        
+
     def test_4_relation_restriction_1(self):
         self._setup_relation_description()
         rset = self.req.execute('Any X WHERE X talks_about Y')
@@ -250,7 +257,7 @@
         self.assertUnorderedIterableEquals([r[0] for r in rset],
                              [self.blog.eid, self.blog2.eid])
         self.assertUnorderedIterableEquals([r[0] for r in rset.description], ['Blog', 'Blog'])
-        
+
     def test_4_relation_restriction_2(self):
         self._setup_relation_description()
         rset = self.req.execute('Any Y WHERE X talks_about Y')
@@ -259,7 +266,7 @@
                              [self.article.eid, self.article2.eid])
         self.assertUnorderedIterableEquals([r[0] for r in rset.description],
                              ['Article', 'Article'])
-        
+
     def test_4_relation_restriction_3(self):
         self._setup_relation_description()
         rset = self.req.execute('Any X,Y WHERE X talks_about Y')
@@ -270,7 +277,7 @@
         self.assertUnorderedIterableEquals([tuple(r) for r in rset.description],
                              [('Blog', 'Article'),
                               ('Blog', 'Article')])
-        
+
     def test_4_relation_restriction_4(self):
         self._setup_relation_description()
         rset = self.req.execute('Any X,Y WHERE X talks_about Y, X eid %(x)s',
@@ -278,7 +285,7 @@
         self._check_rset_size(rset, 1, 2)
         self.assertEquals(rset[0], [self.blog.eid, self.article.eid])
         self.assertUnorderedIterableEquals(rset.description[0], ['Blog', 'Article'])
-        
+
     def test_4_relation_restriction_5(self):
         self._setup_relation_description()
         rset = self.req.execute('Any X,Y WHERE X talks_about Y, Y eid %(x)s',
@@ -286,7 +293,7 @@
         self._check_rset_size(rset, 1, 2)
         self.assertEquals(rset[0], [self.blog.eid, self.article.eid])
         self.assertUnorderedIterableEquals(rset.description[0], ['Blog', 'Article'])
-        
+
     def test_4_relation_subject_restriction(self):
         self._setup_relation_description()
         rset = self.req.execute('Any X,Y WHERE X talks_about Y, X content %(c)s',
@@ -294,7 +301,7 @@
         self._check_rset_size(rset, 1, 2)
         self.assertEquals(rset[0], [self.blog.eid, self.article.eid])
         self.assertUnorderedIterableEquals(rset.description[0], ['Blog', 'Article'])
-        
+
     def test_4_relation_object_restriction(self):
         self._setup_relation_description()
         rset = self.req.execute('Any X WHERE X is Blog, X talks_about Y, Y content %(c)s',
@@ -302,7 +309,7 @@
         self._check_rset_size(rset, 1, 1)
         self.assertEquals(rset[0], [self.blog.eid])
         self.assertUnorderedIterableEquals(rset.description[0], ['Blog'])
-        
+
     def test_4_relation_subject_object_restriction(self):
         article2 = self.add_entity('Article', content=u'very interesting')
         rset = self.req.execute('Any X,XC WHERE X is Blog, X content XC, X content %(xc)s, '
@@ -311,20 +318,20 @@
         self._check_rset_size(rset, 1, 2)
         self.assertEquals(rset[0], [self.blog.eid, self.blog.content])
         self.assertUnorderedIterableEquals(rset.description[0], ['Blog', 'String'])
-        
+
     def test_4_relation_subject_object_restriction_no_res(self):
         article2 = self.add_entity('Article', content=u'very interesting')
         rset = self.req.execute('Any X,XC WHERE X is Blog, X content XC, X content %(xc)s, '
                                 'X talks_about Y, Y content %(c)s',
                                 {'xc': 'hip', 'c': 'very interesting'})
         self.assertEquals(len(rset), 0)
-        
+
     def test_4_relation_subject_object_restriction_no_res_2(self):
         rset = self.req.execute('Any X,XC WHERE X is Blog, X content XC, X content %(xc)s, '
                                 'X talks_about Y, Y content %(c)s',
                                 {'xc': 'hop', 'c': 'not interesting'})
         self.assertEquals(len(rset), 0)
-        
+
     def test_4_relation_restriction_7(self):
         self._setup_relation_description()
         rset = self.req.execute('Any XC,XD,YC WHERE X talks_about Y, Y eid %(x)s,'
@@ -333,7 +340,7 @@
         self._check_rset_size(rset, 1, 3)
         self.assertEquals(rset[0], [self.blog.content, self.blog.diem, self.article.content])
         self.assertUnorderedIterableEquals(rset.description[0], ['String', 'Date', 'String'])
-        
+
     def test_4_relation_restriction_8(self):
         self._setup_relation_description()
         rset = self.req.execute('Any X,Y WHERE X cites Y, Y eid %(x)s', {'x': self.blog.eid})
@@ -346,7 +353,7 @@
         rset = self.req.execute('Any X,Y WHERE X talks_about Y, X eid %(x)s, Y eid %(y)s',
                                 {'x': self.blog.eid, 'y': article2.eid})
         self._check_rset_size(rset, 1, 2)
-        
+
     def test_4_ambiguous_subject_relation(self):
         ye = self.add_entity('YamsEntity')
         self.req.execute('SET X ambiguous_relation Y WHERE X eid %(x)s, Y eid %(y)s',
@@ -365,7 +372,7 @@
         self._check_rset_size(rset, 2, 1)
         self.assertUnorderedIterableEquals([r[0] for r in rset], [self.blog.eid, self.article.eid])
         self.assertUnorderedIterableEquals([r[0] for r in rset.description], ['Blog', 'Article'])
-        
+
     def test_4_relation_selection(self):
         req = self.request()
         rset = req.execute('Any N WHERE G content N, U talks_about G, U eid %(u)s', {'u': self.blog.eid})
@@ -381,7 +388,7 @@
                           [[self.blog3.eid, 'hep'],
                            [self.blog2.eid, 'hip'],
                            [self.blog.eid, 'hop']])
-                           
+
     def test_5_orderby_desc(self):
         self._setup_relation_description()
         rset = self.req.execute('Any X,XC ORDERBY XC DESC WHERE X is Blog, X content XC')
@@ -417,7 +424,7 @@
                           [[self.blog.eid, 'hop', 'personal'],
                            [self.blog2.eid, 'hip', 'personal'],
                            [self.blog3.eid, 'hep', 'business']])
-        
+
     def test_5_orderby_several_terms_mixed_order(self):
         self._setup_relation_description()
         rset = self.req.execute('Any X,XC,XI ORDERBY XI ASC,XC DESC WHERE X is Blog, X content XC, X itemtype XI')
@@ -449,25 +456,25 @@
                                 'WHERE X is Blog, X itemtype XIT')
         self._check_rset_size(rset, 2, 1)
         self.assertEquals(rset.rows, [[self.blog.eid], [blog2.eid]])
-                          
-        
+
+
     def test_6_limit(self):
         self._setup_relation_description()
         rset = self.req.execute('Any X LIMIT 2 WHERE X is Blog')
         self._check_rset_size(rset, 2, 1)
-        
+
     def test_6_offset(self):
         self._setup_relation_description()
         rset = self.req.execute('Any XC ORDERBY XC DESC OFFSET 1 WHERE X is Blog, X content XC')
         self._check_rset_size(rset, 2, 1)
         self.assertEquals(rset.rows, [['hip'], ['hep']])
-        
+
     def test_6_limit_and_orderby(self):
         self._setup_relation_description()
         rset = self.req.execute('Any XC ORDERBY XC LIMIT 2 WHERE X is Blog, X content XC')
         self._check_rset_size(rset, 2, 1)
         self.assertEquals(rset.rows, [['hep'], ['hip']])
-        
+
     def test_6_limit_offset_and_orderby(self):
         self._setup_relation_description()
         rset = self.req.execute('Any XC ORDERBY XC LIMIT 2 OFFSET 0 WHERE X is Blog, X content XC')
@@ -481,7 +488,7 @@
         self.assertEquals(rset.rows, [['hop']])
         rset = self.req.execute('Any XC ORDERBY XC LIMIT 2 OFFSET 3 WHERE X is Blog, X content XC')
         self.failIf(rset)
-        
+
 
     def test_7_simple_datetimecast(self):
         self._setup_relation_description()
@@ -496,7 +503,7 @@
         rset = self.req.execute('Any X WHERE X is Blog, X creation_date <= "%s"'
                                 % _tomorrow.strftime('%Y-%m-%d'))
         self._check_rset_size(rset, 3, 1)
-        
+
     def test_7_identity_relation(self):
         rset = self.req.execute('Any X WHERE X identity Y, X eid %(x)s, Y eid %(y)s',
                                 {'x': self.user.eid, 'y': self.user.eid})
@@ -509,13 +516,13 @@
         rset = self.req.execute('Any X WHERE X identity Y, X eid %(x)s, Y eid %(y)s',
                                 {'x': self.blog.eid, 'y': blog2.eid})
         self.failIf(rset)
-        
+
     def test_8_not_relation_1(self):
         rset = self.req.execute('Any X WHERE X identity U, NOT U in_group G, '
                                 'G name "guests", X eid %(x)s, U eid %(u)s',
                                 {'x': self.user.eid, 'u': self.user.eid})
         self._check_rset_size(rset, 1, 1)
-        self.assertEquals(rset.rows, [[self.user.eid]])        
+        self.assertEquals(rset.rows, [[self.user.eid]])
 
     def test_8_not_relation_linked_subject(self):
         rset = self.req.execute('Any X WHERE NOT X talks_about Y, Y eid %(y)s',
@@ -524,7 +531,7 @@
         blog2 = self.add_entity('Blog', content=u'hop', itemtype=u'personal')
         self.commit()
         rset = self.req.execute('Any X WHERE NOT X talks_about Y, Y eid %(y)s',
-                                {'y': self.article.eid})        
+                                {'y': self.article.eid})
         self._check_rset_size(rset, 1, 1)
         self.assertEquals(rset.rows, [[blog2.eid]])
 
@@ -541,7 +548,7 @@
 
     def test_8_not_relation_linked_attr(self):
         self.skip('not yet implemented')
-        # TODO: this should generated 
+        # TODO: this should generated
         # Query(X)[s_talks_about] > "hop" || Query(X)[s_talks_about] < "hop"
         article2 = self.add_entity('Article', content=u'hop')
         self.req.execute('SET X talks_about Y WHERE X eid %(x)s, Y eid %(y)s',
@@ -564,13 +571,13 @@
         rset = self.req.execute('Any Y WHERE NOT X talks_about Y')
         self._check_rset_size(rset, 1, 1)
         self.assertEquals(rset.rows, [[article2.eid]])
-        
+
     def test_8_not_relation_final_1(self):
         rset = self.req.execute('Any G WHERE G is CWGroup, NOT G name "guests"')
         self._check_rset_size(rset, 2, 1)
         self.assertUnorderedIterableEquals([g.name for g in rset.entities()],
-                                           ['users', 'managers'])        
-        
+                                           ['users', 'managers'])
+
     def test_8_not_relation_final_2(self):
         rset = self.req.execute('Any GN WHERE G is CWGroup, NOT G name "guests", G name GN')
         self._check_rset_size(rset, 2, 1)
@@ -587,8 +594,8 @@
         rset = self.req.execute('Any X WHERE X is Blog, EXISTS(X talks_about Y)')
         self._check_rset_size(rset, 1, 1)
         self.assertEquals(rset.rows, [[self.blog.eid]])
-        
-        
+
+
     def test_error_unknown_eid(self):
         rset = self.req.execute('Any X WHERE X eid %(x)s', {'x': '1234'})
         self.assertEquals(len(rset), 0)
@@ -603,6 +610,6 @@
         rset = self.execute('Any X WHERE Y inlined_relation X, Y eid %(y)s', {'y': eid})
         self._check_rset_size(rset, 1, 1)
         self.assertEquals(rset[0][0], self.blog.eid)
-        
+
 if __name__ == '__main__':
     unittest_main()
--- a/goa/test/unittest_schema.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/test/unittest_schema.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,6 +1,13 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from cubicweb.goa.testlib import *
 
-class Article(db.Model):        
+class Article(db.Model):
     content = db.TextProperty()
     synopsis = db.StringProperty(default='hello')
 
@@ -8,10 +15,10 @@
     diem = db.DateProperty(required=True, auto_now_add=True)
     title = db.StringProperty(required=True)
     content = db.TextProperty()
-    talks_about = db.ReferenceProperty(Article) 
-    cites = db.SelfReferenceProperty() 
+    talks_about = db.ReferenceProperty(Article)
+    cites = db.SelfReferenceProperty()
 
-  
+
 class SomeViewsTC(GAEBasedTC):
     MODEL_CLASSES = (Article, Blog)
 
@@ -77,7 +84,7 @@
                            'is', 'is_instance_of', 'modification_date', 'owned_by'])
         self.assertUnorderedIterableEquals((str(e) for e in eschema.object_relations()),
                              ('identity',))
-    
+
     def test_yams_ambiguous_relation(self):
         rschema = self.schema['ambiguous_relation']
         # only relations defined in the class are actually ordered
@@ -103,7 +110,7 @@
         self.assertEquals(rschema.objects(), ('Bytes',))
         self.assertEquals(rschema.rproperty('Blog', 'Bytes', 'cardinality'), '?1')
 
-        
+
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main
     unittest_main()
--- a/goa/test/unittest_views.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/test/unittest_views.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from cubicweb.goa.testlib import *
 
 from cubicweb.interfaces import ICalendarable
@@ -25,28 +32,28 @@
             return [mydate]
         return []
 
-  
+
 class SomeViewsTC(GAEBasedTC):
     MODEL_CLASSES = (Blog, )
     from cubicweb.web.views import basecontrollers, baseviews, navigation, boxes, calendar
     from data import views
     LOAD_APP_MODULES = (basecontrollers, baseviews, navigation, boxes, calendar, views)
-    
+
     def setUp(self):
         GAEBasedTC.setUp(self)
         self.req = self.request()
         self.blog = Blog(title=u'a blog', content=u'hop')
         self.blog.put(self.req)
-        
+
     def test_hcal(self):
         self.vreg.render('views', 'hcal', self.req, rset=self.blog.rset)
-        
+
     def test_django_index(self):
         self.vreg.render('views', 'index', self.req, rset=None)
 
 for vid in ('primary', 'secondary', 'oneline', 'incontext', 'outofcontext', 'text'):
     setattr(SomeViewsTC, 'test_%s'%vid, lambda self, vid=vid: self.blog.view(vid))
-        
+
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main
     unittest_main()
--- a/goa/testlib.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/testlib.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,11 @@
+"""
+:organization: Logilab
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
+__docformat__ = "restructuredtext en"
+
 from logilab.common.testlib import TestCase
 
 import os, os.path as osp
@@ -18,7 +26,7 @@
     import_appengine_failed = None
 except ImportError, exc:
     # XXX necessary ?
-    class db: 
+    class db:
         class Model:
             pass
         class DummyProperty:
@@ -31,7 +39,7 @@
         ReferenceProperty = DummyProperty
         SelfReferenceProperty = DummyProperty
     import_appengine_failed = 'cannot import appengine: %s' % exc
-    
+
 
 from cubicweb.devtools.fake import FakeRequest
 from cubicweb.goa.goavreg import GAERegistry
@@ -56,11 +64,11 @@
 
     def load_schema_hook(self, loader):
         loader.import_yams_cube_schema('data')
-    
+
     @property
     def DS_FILE(self):
         return self.DS_TEMPL_FILE.replace('-template', '')
-    
+
     @property
     def DS_TEMPL_FILE(self):
         return self._DS_TEMPL_FILE + '_'.join(sorted(cls.__name__ for cls in self.MODEL_CLASSES))
@@ -72,7 +80,7 @@
         stub = datastore_file_stub.DatastoreFileStub(self.APP_ID, dsfile,
                                                      dsfile+'.history')
         apiproxy_stub_map.apiproxy.RegisterStub('datastore_v3', stub)
-        
+
     def setUp(self):
         if import_appengine_failed:
             self.skip(import_appengine_failed)
@@ -88,7 +96,7 @@
             self._set_ds_file(self.DS_TEMPL_FILE)
 #         from google.appengine.api import mail_stub
 #         from google3.apphosting.api import urlfetch_stub
-#         from google3.apphosting.api import user_service_stub        
+#         from google3.apphosting.api import user_service_stub
 #         # Use a fresh stub UserService.
 #         apiproxy_stub_map.apiproxy.RegisterStub(
 #             'user', user_service_stub.UserServiceStub())
@@ -149,15 +157,15 @@
                 req = FakeRequest(vreg=self.vreg)
                 self.session = self.session_manager.open_session(req)
             self.user = self.session.user()
-            
+
     def tearDown(self):
         self.session.close()
-        
+
     def request(self):
         req = FakeRequest(vreg=self.vreg)
         req.set_connection(self.session, self.user)
         return req
-    
+
     def add_entity(self, etype, **kwargs):
         cu = self.session.cursor()
         rql = 'INSERT %s X' % etype
@@ -174,7 +182,7 @@
 
     def rollback(self):
         self.session.rollback()
-        
+
     def create_user(self, login, groups=('users',), req=None):
         assert not self.config['use-google-auth']
         user = self.add_entity('CWUser', upassword=str(login), login=unicode(login))
@@ -190,4 +198,3 @@
         req.form['__login'] = login
         req.form['__password'] = password or login
         return self.session_manager.open_session(req)
-        
--- a/goa/tools/__init__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/tools/__init__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,7 @@
-"""lax tools cube"""
+"""lax tools cube
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
--- a/goa/tools/generate_schema_img.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/tools/generate_schema_img.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,6 +1,13 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 import sys
 from os.path import dirname, abspath, join
-from yams import schema2dot 
+from yams import schema2dot
 
 APPLROOT = abspath(join(dirname(abspath(__file__)), '..'))
 
@@ -9,7 +16,7 @@
 except ImportError:
     sys.path.insert(0, APPLROOT)
     import custom
-    
+
 
 schema = custom.SCHEMA
 skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of')
--- a/goa/tools/laxctl.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/goa/tools/laxctl.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """provides all lax instances management commands into a single utility script
 
 :organization: Logilab
-:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -23,7 +24,7 @@
 
 def initialize_vregistry(applroot):
     # apply monkey patches first
-    from cubicweb.goa import do_monkey_patch    
+    from cubicweb.goa import do_monkey_patch
     do_monkey_patch()
     from cubicweb.goa.goavreg import GAERegistry
     from cubicweb.goa.goaconfig import GAEConfiguration
@@ -32,21 +33,21 @@
     vreg = GAERegistry(config)
     vreg.set_schema(config.load_schema())
     return vreg
-        
+
 def alistdir(directory):
     return [osp.join(directory, f) for f in os.listdir(directory)]
 
 
 class LaxCommand(Command):
     """base command class for all lax commands
-    creates vreg, schema and calls 
+    creates vreg, schema and calls
     """
     min_args = max_args = 0
 
     def run(self, args):
         self.vreg = initialize_vregistry(APPLROOT)
         self._run(args)
-        
+
 
 class GenerateSchemaCommand(LaxCommand):
     """generates the schema's png file"""
@@ -54,7 +55,7 @@
 
     def _run(self, args):
         assert not args, 'no argument expected'
-        from yams import schema2dot        
+        from yams import schema2dot
         schema = self.vreg.schema
         skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of')
         path = osp.join(APPLROOT, 'data', 'schema.png')
@@ -107,7 +108,7 @@
 class GetSessionIdHandler(urllib2.HTTPRedirectHandler):
     def __init__(self, config):
         self.config = config
-        
+
     def http_error_303(self, req, fp, code, msg, headers):
         cookie = SimpleCookie(headers['Set-Cookie'])
         sessionid = cookie['__session'].value
@@ -115,7 +116,7 @@
         setattr(self.config, 'cookie', '__session=' + sessionid)
         return 1 # on exception should be raised
 
-    
+
 class URLCommand(LaxCommand):
     """abstract class for commands doing stuff by accessing the web application
     """
@@ -138,7 +139,7 @@
           'help': 'user password instead of giving raw cookie string (require '
           'lax based authentication).'}),
         )
-    
+
     def _run(self, args):
         baseurl = args[0]
         if not baseurl.startswith('http'):
@@ -154,9 +155,9 @@
             urllib2.install_opener(opener)
             data = urlencode(dict(__login=self.config.user,
                                   __password=self.config.password))
-            self.open_url(urllib2.Request(baseurl, data))            
+            self.open_url(urllib2.Request(baseurl, data))
         opener = urllib2.build_opener(NoRedirectHandler())
-        urllib2.install_opener(opener)        
+        urllib2.install_opener(opener)
         self.do_base_url(baseurl)
 
     def build_req(self, url):
@@ -164,7 +165,7 @@
         if self.config.cookie:
             req.headers['Cookie'] = self.config.cookie
         return req
-    
+
     def open_url(self, req):
         try:
             return urllib2.urlopen(req)
@@ -194,11 +195,11 @@
             msg = remove_html_tags(match.group(1))
             print msg
             return msg
-        
+
     def do_base_url(self, baseurl):
         raise NotImplementedError()
 
-        
+
 class DSInitCommand(URLCommand):
     """initialize the datastore"""
     name = 'db-init'
@@ -210,7 +211,7 @@
           'help': 'number of seconds to wait between each request to avoid '
           'going out of quota.'}),
         )
-        
+
     def do_base_url(self, baseurl):
         req = self.build_req(baseurl + '?vid=contentinit')
         while True:
@@ -240,8 +241,8 @@
         req = self.build_req(baseurl + '?vid=cleansessions')
         data = self.open_url(req)
         self.extract_message(data)
-            
-    
+
+
 register_commands([GenerateSchemaCommand,
                    PopulateDataDirCommand,
                    DSInitCommand,
@@ -250,6 +251,6 @@
 
 def run():
     main_run(sys.argv[1:])
-    
+
 if __name__ == '__main__':
     run()
--- a/hercule.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/hercule.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """RQL client for cubicweb, connecting to application using pyro
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -13,11 +14,11 @@
 from logilab.common.cli import CLIHelper
 from logilab.common.clcommands import BadCommandUsage, pop_arg, register_commands
 from cubicweb.toolsutils import CONNECT_OPTIONS, Command
- 
+
 # result formatter ############################################################
 
 PAGER = os.environ.get('PAGER', 'less')
-            
+
 def pager_format_results(writer, layout):
     """pipe results to a pager like more or less"""
     (r, w) = os.pipe()
@@ -42,8 +43,8 @@
 def izip2(list1, list2):
     for i in xrange(len(list1)):
         yield list1[i] + tuple(list2[i])
-        
-def format_results(writer, layout, stream=sys.stdout): 
+
+def format_results(writer, layout, stream=sys.stdout):
     """format result as text into the given file like object"""
     writer.format(layout, stream)
 
@@ -60,7 +61,7 @@
     return str(value)
 
 # command line querier ########################################################
-    
+
 class RQLCli(CLIHelper):
     """Interactive command line client for CubicWeb, allowing user to execute
     arbitrary RQL queries and to fetch schema information
@@ -74,10 +75,10 @@
         'description'  : "CubicWeb",
         'commit' :       "CubicWeb",
         'rollback' :     "CubicWeb",
-        'autocommit'  :  "Others", 
+        'autocommit'  :  "Others",
         'debug' :        "Others",
         })
-    
+
     def __init__(self, application=None, user=None, password=None,
                  host=None, debug=0):
         CLIHelper.__init__(self, os.path.join(os.environ["HOME"], ".erqlhist"))
@@ -94,7 +95,7 @@
         if application is not None:
             self.do_connect(application, user, password, host)
         self.do_debug(debug)
-        
+
     def do_connect(self, application, user=None, password=None, host=None):
         """connect to an cubicweb application"""
         from cubicweb.dbapi import connect
@@ -113,7 +114,7 @@
         self._completer.list = (self.commands.keys() +
                                 self.schema.entities() + ['Any'])
         print _('You are now connected to %s') % application
-        
+
 
     help_do_connect = ('connect', "connect <application> [<user> [<password> [<host>]]]",
                        _(do_connect.__doc__))
@@ -127,9 +128,9 @@
             self._format = pager_format_results
         if self._debug:
             print _('Debug level set to %s'%debug)
-        
+
     help_do_debug = ('debug', "debug [debug_level]", _(do_debug.__doc__))
-    
+
     def do_description(self):
         """display the description of the latest result"""
         if self.rset.description is None:
@@ -139,7 +140,7 @@
                              for line_desc in self.rset.description])
 
     help_do_description = ('description', "description", _(do_description.__doc__))
-    
+
     def do_schema(self, name=None):
         """display information about the application schema """
         if self.cnx is None:
@@ -159,28 +160,28 @@
                 done = 1
         if done is None:
             print _('Unable to find anything named "%s" in the schema !') % name
-            
+
     help_do_schema = ('schema', "schema [keyword]", _(do_schema.__doc__))
 
-    
+
     def do_commit(self):
         """commit the current transaction"""
         self.cnx.commit()
 
     help_do_commit = ('commit', "commit", _(do_commit.__doc__))
-    
+
     def do_rollback(self):
         """rollback the current transaction"""
         self.cnx.rollback()
 
     help_do_rollback = ('rollback', "rollback", _(do_rollback.__doc__))
-    
+
     def do_autocommit(self):
         """toggle autocommit mode"""
         self.autocommit = not self.autocommit
 
     help_do_autocommit = ('autocommit', "autocommit", _(do_autocommit.__doc__))
-    
+
 
     def handle_line(self, stripped_line):
         """handle non command line :
@@ -232,7 +233,7 @@
     """A command line querier for CubicWeb, using the Relation Query Language.
 
     <application>
-      identifier of the application to connect to 
+      identifier of the application to connect to
     """
     name = 'client'
     arguments = '<application>'
@@ -247,7 +248,7 @@
           'help': 'file containing a batch of RQL statements to execute.',
           }),
         )
-    
+
     def run(self, args):
         """run the command with its specific arguments"""
         appid = pop_arg(args, expected_size_after=None)
@@ -271,5 +272,5 @@
                 cli.handle_line(line)
         else:
             cli.run()
-        
+
 register_commands((CubicWebClientCommand,))
--- a/i18n/en.po	Wed Jun 03 09:09:33 2009 +0200
+++ b/i18n/en.po	Wed Jun 03 09:15:20 2009 +0200
@@ -118,10 +118,6 @@
 msgid "%s software version of the database"
 msgstr ""
 
-#, python-format
-msgid "%s_perm"
-msgstr ""
-
 msgid "**"
 msgstr "0..n 0..n"
 
@@ -200,30 +196,11 @@
 msgid "Bytes_plural"
 msgstr "Bytes"
 
-msgid "Date"
-msgstr "Date"
-
-msgid "Date_plural"
-msgstr "Dates"
-
-msgid "Datetime"
-msgstr "Date and time"
-
-msgid "Datetime_plural"
-msgstr "Dates and times"
-
-#, python-format
-msgid "Debug level set to %s"
-msgstr ""
-
-msgid "Decimal"
-msgstr "Decimal number"
-
-msgid "Decimal_plural"
-msgstr "Decimal numbers"
-
-msgid "Do you want to delete the following element(s) ?"
-msgstr ""
+msgid "CWAttribute"
+msgstr "Attribute"
+
+msgid "CWAttribute_plural"
+msgstr "Attributes"
 
 msgid "CWCache"
 msgstr ""
@@ -249,24 +226,12 @@
 msgid "CWEType_plural"
 msgstr "Entity types"
 
-msgid "CWAttribute"
-msgstr "Attribute"
-
-msgid "CWAttribute_plural"
-msgstr "Attributes"
-
 msgid "CWGroup"
 msgstr "Group"
 
 msgid "CWGroup_plural"
 msgstr "Groups"
 
-msgid "CWRelation"
-msgstr "Relation"
-
-msgid "CWRelation_plural"
-msgstr "Relations"
-
 msgid "CWPermission"
 msgstr "Permission"
 
@@ -285,13 +250,41 @@
 msgid "CWRType_plural"
 msgstr "Relation types"
 
+msgid "CWRelation"
+msgstr "Relation"
+
+msgid "CWRelation_plural"
+msgstr "Relations"
+
 msgid "CWUser"
 msgstr "User"
 
 msgid "CWUser_plural"
 msgstr "Users"
 
-msgid "Email body: "
+msgid "Date"
+msgstr "Date"
+
+msgid "Date_plural"
+msgstr "Dates"
+
+msgid "Datetime"
+msgstr "Date and time"
+
+msgid "Datetime_plural"
+msgstr "Dates and times"
+
+#, python-format
+msgid "Debug level set to %s"
+msgstr ""
+
+msgid "Decimal"
+msgstr "Decimal number"
+
+msgid "Decimal_plural"
+msgstr "Decimal numbers"
+
+msgid "Do you want to delete the following element(s) ?"
 msgstr ""
 
 msgid "EmailAddress"
@@ -312,7 +305,7 @@
 msgid "Float_plural"
 msgstr "Floats"
 
-msgid "From: "
+msgid "From:"
 msgstr ""
 
 msgid "Int"
@@ -330,6 +323,9 @@
 msgid "New Bookmark"
 msgstr "New bookmark"
 
+msgid "New CWAttribute"
+msgstr "New attribute"
+
 msgid "New CWCache"
 msgstr ""
 
@@ -342,15 +338,9 @@
 msgid "New CWEType"
 msgstr "New entity type"
 
-msgid "New CWAttribute"
-msgstr "New attribute"
-
 msgid "New CWGroup"
 msgstr "New group"
 
-msgid "New CWRelation"
-msgstr "New relation"
-
 msgid "New CWPermission"
 msgstr "New permission"
 
@@ -360,6 +350,9 @@
 msgid "New CWRType"
 msgstr "New relation type"
 
+msgid "New CWRelation"
+msgstr "New relation"
+
 msgid "New CWUser"
 msgstr "New user"
 
@@ -396,16 +389,13 @@
 msgid "Please note that this is only a shallow copy"
 msgstr ""
 
-msgid "Problem occured"
-msgstr ""
-
 msgid "RQLExpression"
 msgstr "RQL expression"
 
 msgid "RQLExpression_plural"
 msgstr "RQL expressions"
 
-msgid "Recipients: "
+msgid "Recipients:"
 msgstr ""
 
 msgid "Relations"
@@ -439,7 +429,7 @@
 msgid "String_plural"
 msgstr "Strings"
 
-msgid "Subject: "
+msgid "Subject:"
 msgstr ""
 
 msgid "Submit bug report"
@@ -466,6 +456,9 @@
 msgid "This Bookmark"
 msgstr "This bookmark"
 
+msgid "This CWAttribute"
+msgstr "This attribute"
+
 msgid "This CWCache"
 msgstr ""
 
@@ -478,15 +471,9 @@
 msgid "This CWEType"
 msgstr "This entity type"
 
-msgid "This CWAttribute"
-msgstr "This attribute"
-
 msgid "This CWGroup"
 msgstr "This group"
 
-msgid "This CWRelation"
-msgstr "This relation"
-
 msgid "This CWPermission"
 msgstr "This permission"
 
@@ -496,6 +483,9 @@
 msgid "This CWRType"
 msgstr "This relation type"
 
+msgid "This CWRelation"
+msgstr "This relation"
+
 msgid "This CWUser"
 msgstr "This user"
 
@@ -669,6 +659,12 @@
 msgid "actions_manage_description"
 msgstr ""
 
+msgid "actions_managepermission"
+msgstr ""
+
+msgid "actions_managepermission_description"
+msgstr ""
+
 msgid "actions_muledit"
 msgstr "modify all"
 
@@ -741,6 +737,12 @@
 msgid "add Bookmark bookmarked_by CWUser object"
 msgstr "bookmark"
 
+msgid "add CWAttribute constrained_by CWConstraint subject"
+msgstr "constraint"
+
+msgid "add CWAttribute relation_type CWRType object"
+msgstr "attribute definition"
+
 msgid "add CWEType add_permission RQLExpression subject"
 msgstr "rql expression for the add permission"
 
@@ -753,18 +755,6 @@
 msgid "add CWEType update_permission RQLExpression subject"
 msgstr "rql expression for the update permission"
 
-msgid "add CWAttribute constrained_by CWConstraint subject"
-msgstr "constraint"
-
-msgid "add CWAttribute relation_type CWRType object"
-msgstr "attribute definition"
-
-msgid "add CWRelation constrained_by CWConstraint subject"
-msgstr "constraint"
-
-msgid "add CWRelation relation_type CWRType object"
-msgstr "relation definition"
-
 msgid "add CWProperty for_user CWUser object"
 msgstr "property"
 
@@ -777,6 +767,12 @@
 msgid "add CWRType read_permission RQLExpression subject"
 msgstr "rql expression for the read permission"
 
+msgid "add CWRelation constrained_by CWConstraint subject"
+msgstr "constraint"
+
+msgid "add CWRelation relation_type CWRType object"
+msgstr "relation definition"
+
 msgid "add CWUser in_group CWGroup object"
 msgstr "user"
 
@@ -807,6 +803,9 @@
 msgid "add a Bookmark"
 msgstr "add a bookmark"
 
+msgid "add a CWAttribute"
+msgstr "add an attribute"
+
 msgid "add a CWCache"
 msgstr ""
 
@@ -819,15 +818,9 @@
 msgid "add a CWEType"
 msgstr "add an entity type"
 
-msgid "add a CWAttribute"
-msgstr "add an attribute"
-
 msgid "add a CWGroup"
 msgstr "add a group"
 
-msgid "add a CWRelation"
-msgstr "add a relation"
-
 msgid "add a CWPermission"
 msgstr "add a permission"
 
@@ -837,6 +830,9 @@
 msgid "add a CWRType"
 msgstr "add a relation type"
 
+msgid "add a CWRelation"
+msgstr "add a relation"
+
 msgid "add a CWUser"
 msgstr "add a user"
 
@@ -1118,9 +1114,6 @@
 msgid "click on the box to cancel the deletion"
 msgstr ""
 
-msgid "close all"
-msgstr ""
-
 msgid "comment"
 msgstr ""
 
@@ -1190,12 +1183,6 @@
 msgid "components_rqlinput_description"
 msgstr "the rql box in the page's header"
 
-msgid "components_rss_feed_url"
-msgstr ""
-
-msgid "components_rss_feed_url_description"
-msgstr ""
-
 msgid "composite"
 msgstr ""
 
@@ -1335,28 +1322,31 @@
 msgid "creating Bookmark (Bookmark bookmarked_by CWUser %(linkto)s)"
 msgstr "creating bookmark for %(linkto)s"
 
-msgid "creating CWConstraint (CWAttribute %(linkto)s constrained_by CWConstraint)"
-msgstr "creating constraint for attribute %(linkto)s"
-
-msgid "creating CWConstraint (CWRelation %(linkto)s constrained_by CWConstraint)"
-msgstr "creating constraint for relation %(linkto)s"
-
 msgid "creating CWAttribute (CWAttribute relation_type CWRType %(linkto)s)"
 msgstr "creating attribute %(linkto)s"
 
+msgid ""
+"creating CWConstraint (CWAttribute %(linkto)s constrained_by CWConstraint)"
+msgstr "creating constraint for attribute %(linkto)s"
+
+msgid ""
+"creating CWConstraint (CWRelation %(linkto)s constrained_by CWConstraint)"
+msgstr "creating constraint for relation %(linkto)s"
+
+msgid "creating CWProperty (CWProperty for_user CWUser %(linkto)s)"
+msgstr "creating property for user %(linkto)s"
+
 msgid "creating CWRelation (CWRelation relation_type CWRType %(linkto)s)"
 msgstr "creating relation %(linkto)s"
 
-msgid "creating CWProperty (CWProperty for_user CWUser %(linkto)s)"
-msgstr "creating property for user %(linkto)s"
-
 msgid "creating CWUser (CWUser in_group CWGroup %(linkto)s)"
 msgstr "creating a new user in group %(linkto)s"
 
 msgid "creating EmailAddress (CWUser %(linkto)s use_email EmailAddress)"
 msgstr "creating email address for user %(linkto)s"
 
-msgid "creating RQLExpression (CWEType %(linkto)s add_permission RQLExpression)"
+msgid ""
+"creating RQLExpression (CWEType %(linkto)s add_permission RQLExpression)"
 msgstr "creating rql expression for add permission on %(linkto)s"
 
 msgid ""
@@ -1371,7 +1361,8 @@
 "creating RQLExpression (CWEType %(linkto)s update_permission RQLExpression)"
 msgstr "creating rql expression for update permission on %(linkto)s"
 
-msgid "creating RQLExpression (CWRType %(linkto)s add_permission RQLExpression)"
+msgid ""
+"creating RQLExpression (CWRType %(linkto)s add_permission RQLExpression)"
 msgstr "creating rql expression for add permission on relations %(linkto)s"
 
 msgid ""
@@ -1540,6 +1531,9 @@
 msgid "destination_state_object"
 msgstr "destination of"
 
+msgid "detach attached file"
+msgstr ""
+
 #, python-format
 msgid "detach attached file %s"
 msgstr ""
@@ -1618,9 +1612,18 @@
 msgid "entities deleted"
 msgstr ""
 
+msgid "entity copied"
+msgstr ""
+
+msgid "entity created"
+msgstr ""
+
 msgid "entity deleted"
 msgstr ""
 
+msgid "entity edited"
+msgstr ""
+
 msgid "entity type"
 msgstr ""
 
@@ -1727,6 +1730,10 @@
 msgid "from"
 msgstr ""
 
+#, python-format
+msgid "from %(date)s"
+msgstr ""
+
 msgid "from_entity"
 msgstr "from entity"
 
@@ -1751,6 +1758,9 @@
 msgid "generic plot"
 msgstr ""
 
+msgid "generic relation to link one entity to another"
+msgstr ""
+
 msgid "go back to the index page"
 msgstr ""
 
@@ -2004,6 +2014,11 @@
 msgstr ""
 
 msgid ""
+"link a permission to the entity. This permission should be used in the "
+"security definition of the entity's type to be useful."
+msgstr ""
+
+msgid ""
 "link a property to the user which want this property customization. Unless "
 "you're a site manager, this relation will be handled automatically."
 msgstr ""
@@ -2026,11 +2041,6 @@
 msgid "link a transition to one or more entity type"
 msgstr ""
 
-msgid ""
-"link a transition to one or more rql expression allowing to go through this "
-"transition"
-msgstr ""
-
 msgid "link to each item in"
 msgstr ""
 
@@ -2046,6 +2056,9 @@
 msgid "login"
 msgstr ""
 
+msgid "login or email"
+msgstr ""
+
 msgid "login_action"
 msgstr "log in"
 
@@ -2173,7 +2186,7 @@
 msgid "no"
 msgstr ""
 
-msgid "no associated epermissions"
+msgid "no associated permissions"
 msgstr ""
 
 msgid "no possible transition"
@@ -2362,6 +2375,9 @@
 msgid "remove this Bookmark"
 msgstr "remove this bookmark"
 
+msgid "remove this CWAttribute"
+msgstr "remove this attribute"
+
 msgid "remove this CWCache"
 msgstr ""
 
@@ -2374,15 +2390,9 @@
 msgid "remove this CWEType"
 msgstr "remove this entity type"
 
-msgid "remove this CWAttribute"
-msgstr "remove this attribute"
-
 msgid "remove this CWGroup"
 msgstr "remove this group"
 
-msgid "remove this CWRelation"
-msgstr "remove this relation"
-
 msgid "remove this CWPermission"
 msgstr "remove this permission"
 
@@ -2392,6 +2402,9 @@
 msgid "remove this CWRType"
 msgstr "remove this relation type"
 
+msgid "remove this CWRelation"
+msgstr "remove this relation"
+
 msgid "remove this CWUser"
 msgstr "remove this user"
 
@@ -2416,6 +2429,12 @@
 msgid "require_group_object"
 msgstr "required by"
 
+msgid "require_permission"
+msgstr ""
+
+msgid "require_permission_object"
+msgstr ""
+
 msgid "required attribute"
 msgstr ""
 
@@ -2483,12 +2502,18 @@
 msgid "see them all"
 msgstr ""
 
+msgid "see_also"
+msgstr ""
+
 msgid "select"
 msgstr ""
 
 msgid "select a"
 msgstr ""
 
+msgid "select a key first"
+msgstr ""
+
 msgid "select a relation"
 msgstr ""
 
@@ -2547,6 +2572,9 @@
 msgid "show meta-data"
 msgstr "show the complete schema"
 
+msgid "sioc"
+msgstr ""
+
 msgid "site configuration"
 msgstr ""
 
@@ -2672,6 +2700,10 @@
 msgid "to"
 msgstr ""
 
+#, python-format
+msgid "to %(date)s"
+msgstr ""
+
 msgid "to associate with"
 msgstr ""
 
@@ -2690,6 +2722,9 @@
 msgid "todo_by"
 msgstr "to do by"
 
+msgid "toggle check boxes"
+msgstr ""
+
 msgid "transition is not allowed"
 msgstr ""
 
@@ -2904,6 +2939,9 @@
 msgid "you have been logged out"
 msgstr ""
 
+msgid "you should probably delete that property"
+msgstr ""
+
 #~ msgid "Card"
 #~ msgstr "Card"
 
--- a/i18n/es.po	Wed Jun 03 09:09:33 2009 +0200
+++ b/i18n/es.po	Wed Jun 03 09:15:20 2009 +0200
@@ -123,10 +123,6 @@
 msgid "%s software version of the database"
 msgstr "versión sistema de la base para %s"
 
-#, python-format
-msgid "%s_perm"
-msgstr ""
-
 msgid "**"
 msgstr "0..n 0..n"
 
@@ -205,30 +201,11 @@
 msgid "Bytes_plural"
 msgstr "Bytes"
 
-msgid "Date"
-msgstr "Fecha"
-
-msgid "Date_plural"
-msgstr "Fechas"
-
-msgid "Datetime"
-msgstr "Fecha y hora"
-
-msgid "Datetime_plural"
-msgstr "Fechas y horas"
-
-#, python-format
-msgid "Debug level set to %s"
-msgstr "Nivel de debug puesto a %s"
-
-msgid "Decimal"
-msgstr "Decimal"
-
-msgid "Decimal_plural"
-msgstr "Decimales"
-
-msgid "Do you want to delete the following element(s) ?"
-msgstr "Desea suprimir el(los) elemento(s) siguiente(s)"
+msgid "CWAttribute"
+msgstr "Atributo"
+
+msgid "CWAttribute_plural"
+msgstr "Atributos"
 
 msgid "CWCache"
 msgstr "Cache"
@@ -254,24 +231,12 @@
 msgid "CWEType_plural"
 msgstr "Tipos de entidades"
 
-msgid "CWAttribute"
-msgstr "Atributo"
-
-msgid "CWAttribute_plural"
-msgstr "Atributos"
-
 msgid "CWGroup"
 msgstr "Groupo"
 
 msgid "CWGroup_plural"
 msgstr "Groupos"
 
-msgid "CWRelation"
-msgstr "Relación"
-
-msgid "CWRelation_plural"
-msgstr "Relaciones"
-
 msgid "CWPermission"
 msgstr "Autorización"
 
@@ -290,14 +255,42 @@
 msgid "CWRType_plural"
 msgstr "Tipos de relación"
 
+msgid "CWRelation"
+msgstr "Relación"
+
+msgid "CWRelation_plural"
+msgstr "Relaciones"
+
 msgid "CWUser"
 msgstr "Usuario"
 
 msgid "CWUser_plural"
 msgstr "Usuarios"
 
-msgid "Email body: "
-msgstr "Contenido del correo electrónico : "
+msgid "Date"
+msgstr "Fecha"
+
+msgid "Date_plural"
+msgstr "Fechas"
+
+msgid "Datetime"
+msgstr "Fecha y hora"
+
+msgid "Datetime_plural"
+msgstr "Fechas y horas"
+
+#, python-format
+msgid "Debug level set to %s"
+msgstr "Nivel de debug puesto a %s"
+
+msgid "Decimal"
+msgstr "Decimal"
+
+msgid "Decimal_plural"
+msgstr "Decimales"
+
+msgid "Do you want to delete the following element(s) ?"
+msgstr "Desea suprimir el(los) elemento(s) siguiente(s)"
 
 msgid "EmailAddress"
 msgstr "Correo Electrónico"
@@ -317,8 +310,8 @@
 msgid "Float_plural"
 msgstr "Números flotantes"
 
-msgid "From: "
-msgstr "De : "
+msgid "From:"
+msgstr ""
 
 msgid "Int"
 msgstr "Número entero"
@@ -335,6 +328,9 @@
 msgid "New Bookmark"
 msgstr "Agregar a Favoritos"
 
+msgid "New CWAttribute"
+msgstr "Nueva definición de relación final"
+
 msgid "New CWCache"
 msgstr "Agregar Cache"
 
@@ -347,15 +343,9 @@
 msgid "New CWEType"
 msgstr "Agregar tipo de entidad"
 
-msgid "New CWAttribute"
-msgstr "Nueva definición de relación final"
-
 msgid "New CWGroup"
 msgstr "Nuevo grupo"
 
-msgid "New CWRelation"
-msgstr "Nueva definición de relación final"
-
 msgid "New CWPermission"
 msgstr "Agregar autorización"
 
@@ -365,6 +355,9 @@
 msgid "New CWRType"
 msgstr "Agregar tipo de relación"
 
+msgid "New CWRelation"
+msgstr "Nueva definición de relación final"
+
 msgid "New CWUser"
 msgstr "Agregar usuario"
 
@@ -401,17 +394,14 @@
 msgid "Please note that this is only a shallow copy"
 msgstr "Recuerde que no es más que una copia superficial"
 
-msgid "Problem occured"
-msgstr "Ha ocurrido un error"
-
 msgid "RQLExpression"
 msgstr "Expresión RQL"
 
 msgid "RQLExpression_plural"
 msgstr "Expresiones RQL"
 
-msgid "Recipients: "
-msgstr "Destinatarios : "
+msgid "Recipients:"
+msgstr ""
 
 msgid "Relations"
 msgstr "Relaciones"
@@ -444,8 +434,8 @@
 msgid "String_plural"
 msgstr "Cadenas de caracteres"
 
-msgid "Subject: "
-msgstr "Objeto : "
+msgid "Subject:"
+msgstr ""
 
 msgid "Submit bug report"
 msgstr "Enviar un reporte de error (bug)"
@@ -471,6 +461,9 @@
 msgid "This Bookmark"
 msgstr "Este favorito"
 
+msgid "This CWAttribute"
+msgstr "Esta definición de relación final"
+
 msgid "This CWCache"
 msgstr "Este Cache"
 
@@ -483,15 +476,9 @@
 msgid "This CWEType"
 msgstr "Este tipo de Entidad"
 
-msgid "This CWAttribute"
-msgstr "Esta definición de relación final"
-
 msgid "This CWGroup"
 msgstr "Este grupo"
 
-msgid "This CWRelation"
-msgstr "Esta definición de relación no final"
-
 msgid "This CWPermission"
 msgstr "Esta autorización"
 
@@ -501,6 +488,9 @@
 msgid "This CWRType"
 msgstr "Este tipo de relación"
 
+msgid "This CWRelation"
+msgstr "Esta definición de relación no final"
+
 msgid "This CWUser"
 msgstr "Este usuario"
 
@@ -692,6 +682,12 @@
 msgid "actions_manage_description"
 msgstr ""
 
+msgid "actions_managepermission"
+msgstr ""
+
+msgid "actions_managepermission_description"
+msgstr ""
+
 msgid "actions_muledit"
 msgstr "Edición múltiple"
 
@@ -764,6 +760,12 @@
 msgid "add Bookmark bookmarked_by CWUser object"
 msgstr "Agregar a los favoritos "
 
+msgid "add CWAttribute constrained_by CWConstraint subject"
+msgstr "Restricción"
+
+msgid "add CWAttribute relation_type CWRType object"
+msgstr "Definición de atributo"
+
 msgid "add CWEType add_permission RQLExpression subject"
 msgstr "Agregar una autorización"
 
@@ -776,18 +778,6 @@
 msgid "add CWEType update_permission RQLExpression subject"
 msgstr "Definir una expresión RQL de actualización"
 
-msgid "add CWAttribute constrained_by CWConstraint subject"
-msgstr "Restricción"
-
-msgid "add CWAttribute relation_type CWRType object"
-msgstr "Definición de atributo"
-
-msgid "add CWRelation constrained_by CWConstraint subject"
-msgstr "Restricción"
-
-msgid "add CWRelation relation_type CWRType object"
-msgstr "Definición de relación"
-
 msgid "add CWProperty for_user CWUser object"
 msgstr "Agregar Propiedad"
 
@@ -800,6 +790,12 @@
 msgid "add CWRType read_permission RQLExpression subject"
 msgstr "Agregar expresión RQL de lectura"
 
+msgid "add CWRelation constrained_by CWConstraint subject"
+msgstr "Restricción"
+
+msgid "add CWRelation relation_type CWRType object"
+msgstr "Definición de relación"
+
 msgid "add CWUser in_group CWGroup object"
 msgstr "Agregar usuario"
 
@@ -830,6 +826,9 @@
 msgid "add a Bookmark"
 msgstr "Agregar un Favorito"
 
+msgid "add a CWAttribute"
+msgstr "Agregar un tipo de relación"
+
 msgid "add a CWCache"
 msgstr "Agregar un cache"
 
@@ -842,15 +841,9 @@
 msgid "add a CWEType"
 msgstr "Agregar un tipo de entidad"
 
-msgid "add a CWAttribute"
-msgstr "Agregar un tipo de relación"
-
 msgid "add a CWGroup"
 msgstr "Agregar un grupo de usuarios"
 
-msgid "add a CWRelation"
-msgstr "Agregar una relación"
-
 msgid "add a CWPermission"
 msgstr "Agregar una autorización"
 
@@ -860,6 +853,9 @@
 msgid "add a CWRType"
 msgstr "Agregar un tipo de relación"
 
+msgid "add a CWRelation"
+msgstr "Agregar una relación"
+
 msgid "add a CWUser"
 msgstr "Agregar un usuario"
 
@@ -1148,9 +1144,6 @@
 msgid "click on the box to cancel the deletion"
 msgstr "Seleccione la zona de edición para cancelar la eliminación"
 
-msgid "close all"
-msgstr ""
-
 msgid "comment"
 msgstr "Comentario"
 
@@ -1223,12 +1216,6 @@
 msgid "components_rqlinput_description"
 msgstr "La barra de demanda rql, en el encabezado de página"
 
-msgid "components_rss_feed_url"
-msgstr "RSS FEED URL"
-
-msgid "components_rss_feed_url_description"
-msgstr "El espacio para administrar RSS"
-
 msgid "composite"
 msgstr "composite"
 
@@ -1384,28 +1371,31 @@
 msgid "creating Bookmark (Bookmark bookmarked_by CWUser %(linkto)s)"
 msgstr "Creando Favorito"
 
-msgid "creating CWConstraint (CWAttribute %(linkto)s constrained_by CWConstraint)"
-msgstr "Creación condicionada por el atributo %(linkto)s"
-
-msgid "creating CWConstraint (CWRelation %(linkto)s constrained_by CWConstraint)"
-msgstr "Creación condicionada por la relación %(linkto)s"
-
 msgid "creating CWAttribute (CWAttribute relation_type CWRType %(linkto)s)"
 msgstr "Creación del atributo %(linkto)s"
 
+msgid ""
+"creating CWConstraint (CWAttribute %(linkto)s constrained_by CWConstraint)"
+msgstr "Creación condicionada por el atributo %(linkto)s"
+
+msgid ""
+"creating CWConstraint (CWRelation %(linkto)s constrained_by CWConstraint)"
+msgstr "Creación condicionada por la relación %(linkto)s"
+
+msgid "creating CWProperty (CWProperty for_user CWUser %(linkto)s)"
+msgstr "Creación de una propiedad por el usuario %(linkto)s"
+
 msgid "creating CWRelation (CWRelation relation_type CWRType %(linkto)s)"
 msgstr "Creación de la relación %(linkto)s"
 
-msgid "creating CWProperty (CWProperty for_user CWUser %(linkto)s)"
-msgstr "Creación de una propiedad por el usuario %(linkto)s"
-
 msgid "creating CWUser (CWUser in_group CWGroup %(linkto)s)"
 msgstr "Creación de un usuario para agregar al grupo %(linkto)s"
 
 msgid "creating EmailAddress (CWUser %(linkto)s use_email EmailAddress)"
 msgstr "Creación de una dirección electrónica para el usuario %(linkto)s"
 
-msgid "creating RQLExpression (CWEType %(linkto)s add_permission RQLExpression)"
+msgid ""
+"creating RQLExpression (CWEType %(linkto)s add_permission RQLExpression)"
 msgstr ""
 "Creación de una expresión RQL para la autorización de agregar %(linkto)s"
 
@@ -1422,7 +1412,8 @@
 "creating RQLExpression (CWEType %(linkto)s update_permission RQLExpression)"
 msgstr "Creación de una expresión RQL para autorizar actualizar %(linkto)s"
 
-msgid "creating RQLExpression (CWRType %(linkto)s add_permission RQLExpression)"
+msgid ""
+"creating RQLExpression (CWRType %(linkto)s add_permission RQLExpression)"
 msgstr ""
 "Creación de una expresión RQL para la autorización de agregar relaciones %"
 "(linkto)s"
@@ -1610,6 +1601,9 @@
 msgid "destination_state_object"
 msgstr "Destino de"
 
+msgid "detach attached file"
+msgstr "soltar el archivo existente"
+
 #, python-format
 msgid "detach attached file %s"
 msgstr "Quitar archivo adjunto %s"
@@ -1690,9 +1684,18 @@
 msgid "entities deleted"
 msgstr "Entidades eliminadas"
 
+msgid "entity copied"
+msgstr ""
+
+msgid "entity created"
+msgstr ""
+
 msgid "entity deleted"
 msgstr "Entidad eliminada"
 
+msgid "entity edited"
+msgstr ""
+
 msgid "entity type"
 msgstr "Tipo de entidad"
 
@@ -1804,6 +1807,10 @@
 msgid "from"
 msgstr "De"
 
+#, python-format
+msgid "from %(date)s"
+msgstr ""
+
 msgid "from_entity"
 msgstr "De la entidad"
 
@@ -1828,6 +1835,9 @@
 msgid "generic plot"
 msgstr "Trazado de curbas estándares"
 
+msgid "generic relation to link one entity to another"
+msgstr ""
+
 msgid "go back to the index page"
 msgstr "Regresar a la página de inicio"
 
@@ -2094,6 +2104,11 @@
 msgstr "izquierda"
 
 msgid ""
+"link a permission to the entity. This permission should be used in the "
+"security definition of the entity's type to be useful."
+msgstr ""
+
+msgid ""
 "link a property to the user which want this property customization. Unless "
 "you're a site manager, this relation will be handled automatically."
 msgstr ""
@@ -2119,12 +2134,6 @@
 msgid "link a transition to one or more entity type"
 msgstr "liga una transición a una o mas tipos de entidad"
 
-msgid ""
-"link a transition to one or more rql expression allowing to go through this "
-"transition"
-msgstr ""
-"liga una transición a una o mas expresiones RQL permitiendo que funcione"
-
 msgid "link to each item in"
 msgstr "ligar hacia cada elemento en"
 
@@ -2140,6 +2149,9 @@
 msgid "login"
 msgstr "Clave de acesso"
 
+msgid "login or email"
+msgstr ""
+
 msgid "login_action"
 msgstr "Ingresa tus datos"
 
@@ -2269,8 +2281,8 @@
 msgid "no"
 msgstr "no"
 
-msgid "no associated epermissions"
-msgstr "permisos no asociados"
+msgid "no associated permissions"
+msgstr ""
 
 msgid "no possible transition"
 msgstr "transición no posible"
@@ -2457,6 +2469,9 @@
 msgid "remove this Bookmark"
 msgstr "Eliminar este Favorito"
 
+msgid "remove this CWAttribute"
+msgstr "Eliminar este atributo"
+
 msgid "remove this CWCache"
 msgstr "Eliminar esta cache de aplicación"
 
@@ -2469,15 +2484,9 @@
 msgid "remove this CWEType"
 msgstr "Eliminar este tipo de entidad"
 
-msgid "remove this CWAttribute"
-msgstr "Eliminar este atributo"
-
 msgid "remove this CWGroup"
 msgstr "Eliminar este grupo"
 
-msgid "remove this CWRelation"
-msgstr "Eliminar esta relación"
-
 msgid "remove this CWPermission"
 msgstr "Eliminar este permiso"
 
@@ -2487,6 +2496,9 @@
 msgid "remove this CWRType"
 msgstr "Eliminar esta definición de relación"
 
+msgid "remove this CWRelation"
+msgstr "Eliminar esta relación"
+
 msgid "remove this CWUser"
 msgstr "Eliminar este usuario"
 
@@ -2511,6 +2523,12 @@
 msgid "require_group_object"
 msgstr "Objeto_grupo_requerido"
 
+msgid "require_permission"
+msgstr ""
+
+msgid "require_permission_object"
+msgstr ""
+
 msgid "required attribute"
 msgstr "Atributo requerido"
 
@@ -2582,12 +2600,18 @@
 msgid "see them all"
 msgstr "Ver todos"
 
+msgid "see_also"
+msgstr ""
+
 msgid "select"
 msgstr "Seleccionar"
 
 msgid "select a"
 msgstr "seleccione un"
 
+msgid "select a key first"
+msgstr ""
+
 msgid "select a relation"
 msgstr "seleccione una relación"
 
@@ -2649,6 +2673,9 @@
 msgid "show meta-data"
 msgstr "mostrar meta-data"
 
+msgid "sioc"
+msgstr ""
+
 msgid "site configuration"
 msgstr "configuracion del sitio"
 
@@ -2775,6 +2802,10 @@
 msgid "to"
 msgstr "a"
 
+#, python-format
+msgid "to %(date)s"
+msgstr ""
+
 msgid "to associate with"
 msgstr "a asociar con"
 
@@ -2793,6 +2824,9 @@
 msgid "todo_by"
 msgstr "a hacer por"
 
+msgid "toggle check boxes"
+msgstr ""
+
 msgid "transition is not allowed"
 msgstr "transition no permitida"
 
@@ -3020,6 +3054,9 @@
 msgid "you have been logged out"
 msgstr "ha terminado la sesion"
 
+msgid "you should probably delete that property"
+msgstr ""
+
 #~ msgid "%s constraint failed"
 #~ msgstr "La contrainte %s n'est pas satisfaite"
 
@@ -3035,15 +3072,30 @@
 #~ msgid "Card_plural"
 #~ msgstr "Fichas"
 
+#~ msgid "Email body: "
+#~ msgstr "Contenido del correo electrónico : "
+
+#~ msgid "From: "
+#~ msgstr "De : "
+
 #~ msgid "Loading"
 #~ msgstr "chargement"
 
 #~ msgid "New Card"
 #~ msgstr "Agregar Ficha"
 
+#~ msgid "Problem occured"
+#~ msgstr "Ha ocurrido un error"
+
 #~ msgid "Problem occured while setting new value"
 #~ msgstr "Un problËme est survenu lors de la mise ‡ jour"
 
+#~ msgid "Recipients: "
+#~ msgstr "Destinatarios : "
+
+#~ msgid "Subject: "
+#~ msgstr "Objeto : "
+
 #~ msgid "This Card"
 #~ msgstr "Esta Ficha"
 
@@ -3069,6 +3121,12 @@
 #~ msgid "cancel edition"
 #~ msgstr "annuler l'Èdition"
 
+#~ msgid "components_rss_feed_url"
+#~ msgstr "RSS FEED URL"
+
+#~ msgid "components_rss_feed_url_description"
+#~ msgstr "El espacio para administrar RSS"
+
 #~ msgid "content"
 #~ msgstr "Contenido"
 
@@ -3082,9 +3140,6 @@
 #~ "langue par dÈfaut (regarder le rÈpertoire i18n de l'application pour voir "
 #~ "les langues disponibles)"
 
-#~ msgid "detach attached file"
-#~ msgstr "soltar el archivo existente"
-
 #~ msgid "filter"
 #~ msgstr "filtrer"
 
@@ -3103,6 +3158,12 @@
 #~ msgid "inlined view"
 #~ msgstr "Vista incluída (en línea)"
 
+#~ msgid ""
+#~ "link a transition to one or more rql expression allowing to go through "
+#~ "this transition"
+#~ msgstr ""
+#~ "liga una transición a una o mas expresiones RQL permitiendo que funcione"
+
 #~ msgid "linked"
 #~ msgstr "liÈ"
 
@@ -3114,6 +3175,9 @@
 #~ msgstr ""
 #~ "nombre maximum d'entitÈs liÈes ‡ afficher dans la vue de restriction"
 
+#~ msgid "no associated epermissions"
+#~ msgstr "permisos no asociados"
+
 #~ msgid "owned by"
 #~ msgstr "appartient ‡"
 
--- a/i18n/fr.po	Wed Jun 03 09:09:33 2009 +0200
+++ b/i18n/fr.po	Wed Jun 03 09:15:20 2009 +0200
@@ -123,10 +123,6 @@
 msgid "%s software version of the database"
 msgstr "version logicielle de la base pour %s"
 
-#, python-format
-msgid "%s_perm"
-msgstr ""
-
 msgid "**"
 msgstr "0..n 0..n"
 
@@ -205,30 +201,11 @@
 msgid "Bytes_plural"
 msgstr "Données binaires"
 
-msgid "Date"
-msgstr "Date"
-
-msgid "Date_plural"
-msgstr "Dates"
-
-msgid "Datetime"
-msgstr "Date et heure"
-
-msgid "Datetime_plural"
-msgstr "Date et heure"
-
-#, python-format
-msgid "Debug level set to %s"
-msgstr "Niveau de debug mis à %s"
-
-msgid "Decimal"
-msgstr "Nombre décimal"
-
-msgid "Decimal_plural"
-msgstr "Nombres décimaux"
-
-msgid "Do you want to delete the following element(s) ?"
-msgstr "Voulez vous supprimer le(s) élément(s) suivant(s)"
+msgid "CWAttribute"
+msgstr "Attribut"
+
+msgid "CWAttribute_plural"
+msgstr "Attributs"
 
 msgid "CWCache"
 msgstr "Cache applicatif"
@@ -254,24 +231,12 @@
 msgid "CWEType_plural"
 msgstr "Types d'entité"
 
-msgid "CWAttribute"
-msgstr "Attribut"
-
-msgid "CWAttribute_plural"
-msgstr "Attributs"
-
 msgid "CWGroup"
 msgstr "Groupe"
 
 msgid "CWGroup_plural"
 msgstr "Groupes"
 
-msgid "CWRelation"
-msgstr "Relation"
-
-msgid "CWRelation_plural"
-msgstr "Relations"
-
 msgid "CWPermission"
 msgstr "Permission"
 
@@ -290,14 +255,42 @@
 msgid "CWRType_plural"
 msgstr "Types de relation"
 
+msgid "CWRelation"
+msgstr "Relation"
+
+msgid "CWRelation_plural"
+msgstr "Relations"
+
 msgid "CWUser"
 msgstr "Utilisateur"
 
 msgid "CWUser_plural"
 msgstr "Utilisateurs"
 
-msgid "Email body: "
-msgstr "Contenu du courriel : "
+msgid "Date"
+msgstr "Date"
+
+msgid "Date_plural"
+msgstr "Dates"
+
+msgid "Datetime"
+msgstr "Date et heure"
+
+msgid "Datetime_plural"
+msgstr "Date et heure"
+
+#, python-format
+msgid "Debug level set to %s"
+msgstr "Niveau de debug mis à %s"
+
+msgid "Decimal"
+msgstr "Nombre décimal"
+
+msgid "Decimal_plural"
+msgstr "Nombres décimaux"
+
+msgid "Do you want to delete the following element(s) ?"
+msgstr "Voulez vous supprimer le(s) élément(s) suivant(s)"
 
 msgid "EmailAddress"
 msgstr "Adresse électronique"
@@ -317,8 +310,8 @@
 msgid "Float_plural"
 msgstr "Nombres flottants"
 
-msgid "From: "
-msgstr "De : "
+msgid "From:"
+msgstr "De :"
 
 msgid "Int"
 msgstr "Nombre entier"
@@ -335,6 +328,9 @@
 msgid "New Bookmark"
 msgstr "Nouveau signet"
 
+msgid "New CWAttribute"
+msgstr "Nouvelle définition de relation finale"
+
 msgid "New CWCache"
 msgstr "Nouveau cache applicatif"
 
@@ -347,15 +343,9 @@
 msgid "New CWEType"
 msgstr "Nouveau type d'entité"
 
-msgid "New CWAttribute"
-msgstr "Nouvelle définition de relation finale"
-
 msgid "New CWGroup"
 msgstr "Nouveau groupe"
 
-msgid "New CWRelation"
-msgstr "Nouvelle définition de relation non finale"
-
 msgid "New CWPermission"
 msgstr "Nouvelle permission"
 
@@ -365,6 +355,9 @@
 msgid "New CWRType"
 msgstr "Nouveau type de relation"
 
+msgid "New CWRelation"
+msgstr "Nouvelle définition de relation non finale"
+
 msgid "New CWUser"
 msgstr "Nouvel utilisateur"
 
@@ -401,17 +394,14 @@
 msgid "Please note that this is only a shallow copy"
 msgstr "Attention, cela n'effectue qu'une copie de surface"
 
-msgid "Problem occured"
-msgstr "Une erreur est survenue"
-
 msgid "RQLExpression"
 msgstr "Expression RQL"
 
 msgid "RQLExpression_plural"
 msgstr "Expressions RQL"
 
-msgid "Recipients: "
-msgstr "Destinataires : "
+msgid "Recipients:"
+msgstr "Destinataires :"
 
 msgid "Relations"
 msgstr "Relations"
@@ -444,8 +434,8 @@
 msgid "String_plural"
 msgstr "Chaînes de caractères"
 
-msgid "Subject: "
-msgstr "Sujet : "
+msgid "Subject:"
+msgstr "Sujet :"
 
 msgid "Submit bug report"
 msgstr "Soumettre un rapport de bug"
@@ -471,6 +461,9 @@
 msgid "This Bookmark"
 msgstr "Ce signet"
 
+msgid "This CWAttribute"
+msgstr "Cette définition de relation finale"
+
 msgid "This CWCache"
 msgstr "Ce cache applicatif"
 
@@ -483,15 +476,9 @@
 msgid "This CWEType"
 msgstr "Ce type d'entité"
 
-msgid "This CWAttribute"
-msgstr "Cette définition de relation finale"
-
 msgid "This CWGroup"
 msgstr "Ce groupe"
 
-msgid "This CWRelation"
-msgstr "Cette définition de relation non finale"
-
 msgid "This CWPermission"
 msgstr "Cette permission"
 
@@ -501,6 +488,9 @@
 msgid "This CWRType"
 msgstr "Ce type de relation"
 
+msgid "This CWRelation"
+msgstr "Cette définition de relation non finale"
+
 msgid "This CWUser"
 msgstr "Cet utilisateur"
 
@@ -690,6 +680,12 @@
 msgid "actions_manage_description"
 msgstr ""
 
+msgid "actions_managepermission"
+msgstr ""
+
+msgid "actions_managepermission_description"
+msgstr ""
+
 msgid "actions_muledit"
 msgstr "édition multiple"
 
@@ -762,6 +758,12 @@
 msgid "add Bookmark bookmarked_by CWUser object"
 msgstr "signet"
 
+msgid "add CWAttribute constrained_by CWConstraint subject"
+msgstr "contrainte"
+
+msgid "add CWAttribute relation_type CWRType object"
+msgstr "définition d'attribut"
+
 msgid "add CWEType add_permission RQLExpression subject"
 msgstr "définir une expression RQL d'ajout"
 
@@ -774,18 +776,6 @@
 msgid "add CWEType update_permission RQLExpression subject"
 msgstr "définir une expression RQL de mise à jour"
 
-msgid "add CWAttribute constrained_by CWConstraint subject"
-msgstr "contrainte"
-
-msgid "add CWAttribute relation_type CWRType object"
-msgstr "définition d'attribut"
-
-msgid "add CWRelation constrained_by CWConstraint subject"
-msgstr "contrainte"
-
-msgid "add CWRelation relation_type CWRType object"
-msgstr "définition de relation"
-
 msgid "add CWProperty for_user CWUser object"
 msgstr "propriété"
 
@@ -798,6 +788,12 @@
 msgid "add CWRType read_permission RQLExpression subject"
 msgstr "expression RQL de lecture"
 
+msgid "add CWRelation constrained_by CWConstraint subject"
+msgstr "contrainte"
+
+msgid "add CWRelation relation_type CWRType object"
+msgstr "définition de relation"
+
 msgid "add CWUser in_group CWGroup object"
 msgstr "utilisateur"
 
@@ -828,6 +824,9 @@
 msgid "add a Bookmark"
 msgstr "ajouter un signet"
 
+msgid "add a CWAttribute"
+msgstr "ajouter un type de relation"
+
 msgid "add a CWCache"
 msgstr "ajouter un cache applicatif"
 
@@ -840,15 +839,9 @@
 msgid "add a CWEType"
 msgstr "ajouter un type d'entité"
 
-msgid "add a CWAttribute"
-msgstr "ajouter un type de relation"
-
 msgid "add a CWGroup"
 msgstr "ajouter un groupe d'utilisateurs"
 
-msgid "add a CWRelation"
-msgstr "ajouter une relation"
-
 msgid "add a CWPermission"
 msgstr "ajouter une permission"
 
@@ -858,6 +851,9 @@
 msgid "add a CWRType"
 msgstr "ajouter un type de relation"
 
+msgid "add a CWRelation"
+msgstr "ajouter une relation"
+
 msgid "add a CWUser"
 msgstr "ajouter un utilisateur"
 
@@ -1146,9 +1142,6 @@
 msgid "click on the box to cancel the deletion"
 msgstr "cliquer dans la zone d'édition pour annuler la suppression"
 
-msgid "close all"
-msgstr "tout fermer"
-
 msgid "comment"
 msgstr "commentaire"
 
@@ -1221,12 +1214,6 @@
 msgid "components_rqlinput_description"
 msgstr "la barre de requête rql, dans l'en-tête de page"
 
-msgid "components_rss_feed_url"
-msgstr "syndication rss"
-
-msgid "components_rss_feed_url_description"
-msgstr ""
-
 msgid "composite"
 msgstr "composite"
 
@@ -1383,28 +1370,31 @@
 msgid "creating Bookmark (Bookmark bookmarked_by CWUser %(linkto)s)"
 msgstr "création d'un signet pour %(linkto)s"
 
-msgid "creating CWConstraint (CWAttribute %(linkto)s constrained_by CWConstraint)"
-msgstr "création d'une contrainte pour l'attribut %(linkto)s"
-
-msgid "creating CWConstraint (CWRelation %(linkto)s constrained_by CWConstraint)"
-msgstr "création d'une contrainte pour la relation %(linkto)s"
-
 msgid "creating CWAttribute (CWAttribute relation_type CWRType %(linkto)s)"
 msgstr "création d'un attribut %(linkto)s"
 
+msgid ""
+"creating CWConstraint (CWAttribute %(linkto)s constrained_by CWConstraint)"
+msgstr "création d'une contrainte pour l'attribut %(linkto)s"
+
+msgid ""
+"creating CWConstraint (CWRelation %(linkto)s constrained_by CWConstraint)"
+msgstr "création d'une contrainte pour la relation %(linkto)s"
+
+msgid "creating CWProperty (CWProperty for_user CWUser %(linkto)s)"
+msgstr "création d'une propriété pour l'utilisateur %(linkto)s"
+
 msgid "creating CWRelation (CWRelation relation_type CWRType %(linkto)s)"
 msgstr "création relation %(linkto)s"
 
-msgid "creating CWProperty (CWProperty for_user CWUser %(linkto)s)"
-msgstr "création d'une propriété pour l'utilisateur %(linkto)s"
-
 msgid "creating CWUser (CWUser in_group CWGroup %(linkto)s)"
 msgstr "création d'un utilisateur à rajouter au groupe %(linkto)s"
 
 msgid "creating EmailAddress (CWUser %(linkto)s use_email EmailAddress)"
 msgstr "création d'une adresse électronique pour l'utilisateur %(linkto)s"
 
-msgid "creating RQLExpression (CWEType %(linkto)s add_permission RQLExpression)"
+msgid ""
+"creating RQLExpression (CWEType %(linkto)s add_permission RQLExpression)"
 msgstr "création d'une expression RQL pour la permission d'ajout de %(linkto)s"
 
 msgid ""
@@ -1421,7 +1411,8 @@
 msgstr ""
 "création d'une expression RQL pour la permission de mise à jour de %(linkto)s"
 
-msgid "creating RQLExpression (CWRType %(linkto)s add_permission RQLExpression)"
+msgid ""
+"creating RQLExpression (CWRType %(linkto)s add_permission RQLExpression)"
 msgstr ""
 "création d'une expression RQL pour la permission d'ajout des relations %"
 "(linkto)s"
@@ -1608,6 +1599,9 @@
 msgid "destination_state_object"
 msgstr "destination de"
 
+msgid "detach attached file"
+msgstr "détacher le fichier existant"
+
 #, python-format
 msgid "detach attached file %s"
 msgstr "détacher le fichier existant %s"
@@ -1647,7 +1641,7 @@
 msgstr "icône de téléchargement"
 
 msgid "download schema as owl"
-msgstr ""
+msgstr "télécharger le schéma OWL"
 
 msgid "edit bookmarks"
 msgstr "éditer les signets"
@@ -1688,9 +1682,18 @@
 msgid "entities deleted"
 msgstr "entités supprimées"
 
+msgid "entity copied"
+msgstr "entité copiée"
+
+msgid "entity created"
+msgstr "entité créée"
+
 msgid "entity deleted"
 msgstr "entité supprimée"
 
+msgid "entity edited"
+msgstr "entité éditée"
+
 msgid "entity type"
 msgstr "type d'entité"
 
@@ -1801,6 +1804,10 @@
 msgid "from"
 msgstr "de"
 
+#, python-format
+msgid "from %(date)s"
+msgstr "du %(date)s"
+
 msgid "from_entity"
 msgstr "de l'entité"
 
@@ -1825,6 +1832,9 @@
 msgid "generic plot"
 msgstr "tracé de courbes standard"
 
+msgid "generic relation to link one entity to another"
+msgstr "relation générique pour lier une entité à une autre"
+
 msgid "go back to the index page"
 msgstr "retourner sur la page d'accueil"
 
@@ -2093,6 +2103,13 @@
 msgstr "gauche"
 
 msgid ""
+"link a permission to the entity. This permission should be used in the "
+"security definition of the entity's type to be useful."
+msgstr ""
+"lie une permission à une entité. Cette permission doit généralement être utilisée "
+"dans la définition de sécurité du type d'entité pour être utile."
+
+msgid ""
 "link a property to the user which want this property customization. Unless "
 "you're a site manager, this relation will be handled automatically."
 msgstr ""
@@ -2118,11 +2135,6 @@
 msgid "link a transition to one or more entity type"
 msgstr "lie une transition à un ou plusieurs types d'entités"
 
-msgid ""
-"link a transition to one or more rql expression allowing to go through this "
-"transition"
-msgstr ""
-
 msgid "link to each item in"
 msgstr "lier vers chaque élément dans"
 
@@ -2138,6 +2150,9 @@
 msgid "login"
 msgstr "identifiant"
 
+msgid "login or email"
+msgstr "identifiant ou email"
+
 msgid "login_action"
 msgstr "identifiez vous"
 
@@ -2267,8 +2282,8 @@
 msgid "no"
 msgstr "non"
 
-msgid "no associated epermissions"
-msgstr "aucune permission spécifique n'est définie"
+msgid "no associated permissions"
+msgstr "aucune permission associée"
 
 msgid "no possible transition"
 msgstr "aucune transition possible"
@@ -2335,10 +2350,10 @@
 msgstr "ordre"
 
 msgid "owl"
-msgstr ""
+msgstr "owl"
 
 msgid "owlabox"
-msgstr ""
+msgstr "owl ABox"
 
 msgid "owned_by"
 msgstr "appartient à"
@@ -2446,7 +2461,7 @@
 msgstr "définition"
 
 msgid "relations"
-msgstr ""
+msgstr "relations"
 
 msgid "relations deleted"
 msgstr "relations supprimées"
@@ -2457,6 +2472,9 @@
 msgid "remove this Bookmark"
 msgstr "supprimer ce signet"
 
+msgid "remove this CWAttribute"
+msgstr "supprimer cet attribut"
+
 msgid "remove this CWCache"
 msgstr "supprimer ce cache applicatif"
 
@@ -2469,15 +2487,9 @@
 msgid "remove this CWEType"
 msgstr "supprimer ce type d'entité"
 
-msgid "remove this CWAttribute"
-msgstr "supprimer cet attribut"
-
 msgid "remove this CWGroup"
 msgstr "supprimer ce groupe"
 
-msgid "remove this CWRelation"
-msgstr "supprimer cette relation"
-
 msgid "remove this CWPermission"
 msgstr "supprimer cette permission"
 
@@ -2487,6 +2499,9 @@
 msgid "remove this CWRType"
 msgstr "supprimer cette définition de relation"
 
+msgid "remove this CWRelation"
+msgstr "supprimer cette relation"
+
 msgid "remove this CWUser"
 msgstr "supprimer cet utilisateur"
 
@@ -2511,6 +2526,12 @@
 msgid "require_group_object"
 msgstr "à les droits"
 
+msgid "require_permission"
+msgstr "require permission"
+
+msgid "require_permission_object"
+msgstr "permission of"
+
 msgid "required attribute"
 msgstr "attribut requis"
 
@@ -2587,12 +2608,18 @@
 msgid "see them all"
 msgstr "les voir toutes"
 
+msgid "see_also"
+msgstr "see also"
+
 msgid "select"
 msgstr "sélectionner"
 
 msgid "select a"
 msgstr "sélectionner un"
 
+msgid "select a key first"
+msgstr "sélectionnez d'abord une clé"
+
 msgid "select a relation"
 msgstr "sélectionner une relation"
 
@@ -2654,6 +2681,9 @@
 msgid "show meta-data"
 msgstr "afficher le schéma complet"
 
+msgid "sioc"
+msgstr "sioc"
+
 msgid "site configuration"
 msgstr "configuration du site"
 
@@ -2780,6 +2810,10 @@
 msgid "to"
 msgstr "à"
 
+#, python-format
+msgid "to %(date)s"
+msgstr "au %(date)s"
+
 msgid "to associate with"
 msgstr "pour associer à"
 
@@ -2798,6 +2832,9 @@
 msgid "todo_by"
 msgstr "à faire par"
 
+msgid "toggle check boxes"
+msgstr "inverser les cases à cocher"
+
 msgid "transition is not allowed"
 msgstr "transition non permise"
 
@@ -2865,7 +2902,7 @@
 msgstr "clé de propriété inconnue"
 
 msgid "up"
-msgstr ""
+msgstr "haut"
 
 msgid "upassword"
 msgstr "mot de passe"
@@ -3023,6 +3060,9 @@
 msgid "you have been logged out"
 msgstr "vous avez été déconnecté"
 
+msgid "you should probably delete that property"
+msgstr "vous devriez probablement supprimer cette propriété"
+
 #~ msgid "%s constraint failed"
 #~ msgstr "La contrainte %s n'est pas satisfaite"
 
@@ -3038,15 +3078,30 @@
 #~ msgid "Card_plural"
 #~ msgstr "Fiches"
 
+#~ msgid "Email body: "
+#~ msgstr "Contenu du courriel : "
+
+#~ msgid "From: "
+#~ msgstr "De : "
+
 #~ msgid "Loading"
 #~ msgstr "chargement"
 
 #~ msgid "New Card"
 #~ msgstr "Nouvelle fiche"
 
+#~ msgid "Problem occured"
+#~ msgstr "Une erreur est survenue"
+
 #~ msgid "Problem occured while setting new value"
 #~ msgstr "Un problème est survenu lors de la mise à jour"
 
+#~ msgid "Recipients: "
+#~ msgstr "Destinataires : "
+
+#~ msgid "Subject: "
+#~ msgstr "Sujet : "
+
 #~ msgid "This Card"
 #~ msgstr "Cette fiche"
 
@@ -3075,6 +3130,12 @@
 #~ msgid "cancel edition"
 #~ msgstr "annuler l'édition"
 
+#~ msgid "close all"
+#~ msgstr "tout fermer"
+
+#~ msgid "components_rss_feed_url"
+#~ msgstr "syndication rss"
+
 #~ msgid "content"
 #~ msgstr "contenu"
 
@@ -3088,9 +3149,6 @@
 #~ "langue par défaut (regarder le répertoire i18n de l'application pour voir "
 #~ "les langues disponibles)"
 
-#~ msgid "detach attached file"
-#~ msgstr "détacher le fichier existant"
-
 #~ msgid "filter"
 #~ msgstr "filtrer"
 
@@ -3120,6 +3178,9 @@
 #~ msgstr ""
 #~ "nombre maximum d'entités liées à afficher dans la vue de restriction"
 
+#~ msgid "no associated epermissions"
+#~ msgstr "aucune permission spécifique n'est définie"
+
 #~ msgid "owned by"
 #~ msgstr "appartient à"
 
--- a/interfaces.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/interfaces.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Specific views for entities implementing IDownloadable
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 __docformat__ = "restructuredtext en"
@@ -267,6 +268,3 @@
 
     def isioc_items(self):
         """return contained items"""
-
-
-
--- a/md5crypt.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/md5crypt.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,11 @@
 #########################################################
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 # md5crypt.py
 #
 # 0423.2000 by michal wallace http://www.sabren.com/
--- a/misc/cwdesklets/rqlsensor/__init__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/misc/cwdesklets/rqlsensor/__init__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 import webbrowser
 reload(webbrowser)
 
@@ -52,7 +59,7 @@
             output.set('resultbg[%s]' % index, 'black')
             webbrowser.open(self._urls[index])
         self._send_output(output)
-        
+
     def __get_connection(self):
         try:
             return self._v_cnx
@@ -73,8 +80,8 @@
             del self._v_cnx
             raise
         self._urls = []
-        output.set('layout', 'vertical, 14')        
-        output.set('length', rset.rowcount)        
+        output.set('layout', 'vertical, 14')
+        output.set('length', rset.rowcount)
         i = 0
         for line in rset:
             output.set('result[%s]' % i, ', '.join([str(v) for v in line[1:]]))
@@ -84,7 +91,7 @@
             except:
                 self._urls.append('')
             i += 1
-    
+
     def __update(self):
         output = self._new_output()
         try:
@@ -92,12 +99,12 @@
         except Exception, ex:
             import traceback
             traceback.print_exc()
-            output.set('layout', 'vertical, 10')        
-            output.set('length', 1)        
+            output.set('layout', 'vertical, 10')
+            output.set('length', 1)
             output.set('result[0]', str(ex))
         self._send_output(output)
         self._add_timer(int(self._get_config('delay'))*1000, self.__update)
 
-        
+
 def new_sensor(args):
     return RQLSensor(*args)
--- a/misc/cwfs/cwfs.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/misc/cwfs/cwfs.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 class Schema :
 
     def __init__(self, schema) :
@@ -24,7 +31,7 @@
                                ] ),
                  })
 
-    
+
 
 DATA = { 'societe': [ ('CETIAD', 'Dijon'),
                       ('EDF_R&D', 'Clamart'),
@@ -58,7 +65,7 @@
         self._attr = None
         self._rel = None
         self._restrictions = []
-        
+
     def parse(self) :
         self._entity = self._components.next()
         try:
@@ -97,7 +104,7 @@
             for nom, entity in self.schema.get_relations(self._entity) :
                 yield nom+'/'
                 yield entity+'/'
-    
+
 def ls(path) :
     p = PathParser(SCHEMA,path)
     p.parse()
@@ -113,7 +120,7 @@
         self._e_type = None
         self._restrictions = []
         self._alphabet = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
-        
+
     def parse(self):
         self._var = self._alphabet.pop(0)
         self._e_type = self._components.next()
@@ -124,7 +131,7 @@
         except StopIteration :
             pass
         return 'Any %s WHERE %s' % (self._var, ', '.join(self._restrictions))
-    
+
     def process_entity(self) :
         _next = self._components.next()
         if _next in self.schema.get_attrs(self._e_type) :
@@ -147,9 +154,9 @@
                 self._restrictions.append('%s is %s' % (r_var, _next.capitalize()))
             except StopIteration:
                 raise
-        self.process_entity()            
+        self.process_entity()
 
-        
+
 def to_rql(path) :
     p = SytPathParser(SCHEMA,path)
     return p.parse()
--- a/misc/cwfs/cwfs_test.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/misc/cwfs/cwfs_test.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from logilab.common.testlib import TestCase, unittest_main
 
 import cubicwebfs
--- a/misc/cwzope/cwzope.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/misc/cwzope/cwzope.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from AccessControl import getSecurityManager 
 
 from cubicweb.dbapi import connect, Connection, Cursor
--- a/misc/migration/2.42.0_Any.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/misc/migration/2.42.0_Any.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,2 +1,9 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 synchronize_rschema('created_by')
 synchronize_rschema('owned_by')
--- a/misc/migration/2.42.1_Any.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/misc/migration/2.42.1_Any.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 if confirm('remove deprecated database constraints?'):
     execute = session.system_sql
     session.set_pool()
@@ -15,4 +22,4 @@
 if 'inline_view' in schema:
     # inline_view attribute should have been deleted for a while now....
     drop_attribute('CWRelation', 'inline_view')
-    
+
--- a/misc/migration/2.43.0_Any.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/misc/migration/2.43.0_Any.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,8 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 synchronize_permissions('EmailAddress')
--- a/misc/migration/2.44.0_Any.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/misc/migration/2.44.0_Any.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 change_relation_props('CWAttribute', 'cardinality', 'String', internationalizable=True)
 change_relation_props('CWRelation', 'cardinality', 'String', internationalizable=True)
 
--- a/misc/migration/2.45.0_Any.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/misc/migration/2.45.0_Any.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,11 @@
 # following functions have been renamed, but keep old definition for bw compat
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 sql('''CREATE AGGREGATE group_concat (
   basetype = anyelement,
   sfunc = array_append,
--- a/misc/migration/2.46.0_Any.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/misc/migration/2.46.0_Any.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 
 rql('SET X value "navtop" WHERE X pkey ~= "contentnavigation.%.context", X value "header"')
--- a/misc/migration/2.47.0_Any.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/misc/migration/2.47.0_Any.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 synchronize_permissions('primary_email')
 synchronize_rschema('wf_info_for')
 synchronize_rschema('use_email')
--- a/misc/migration/2.48.8_Any.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/misc/migration/2.48.8_Any.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,2 +1,9 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 for etype in ('CWRType', 'CWAttribute', 'CWRelation', 'CWConstraint', 'CWConstraintType'):
     synchronize_permissions(etype)
--- a/misc/migration/2.49.3_Any.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/misc/migration/2.49.3_Any.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,8 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 add_entity_type('Decimal')
--- a/misc/migration/2.50.0_Any.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/misc/migration/2.50.0_Any.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,8 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 add_relation_type('specializes')
--- a/misc/migration/2.99.0_Any.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/misc/migration/2.99.0_Any.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from cubicweb import CW_MIGRATION_MAP
 
 for pk, in rql('Any K WHERE X is CWProperty, X pkey IN (%s), X pkey K'
--- a/misc/migration/3.1.5_Any.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/misc/migration/3.1.5_Any.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,8 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 synchronize_permissions('condition')
--- a/misc/migration/3.2.0_Any.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/misc/migration/3.2.0_Any.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 rql('SET X value "main-template" WHERE X is CWProperty, '
     'X pkey "ui.main-template", X value "main"')
 checkpoint()
--- a/misc/migration/bootstrapmigration_repository.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/misc/migration/bootstrapmigration_repository.py	Wed Jun 03 09:15:20 2009 +0200
@@ -3,10 +3,20 @@
 it should only include low level schema changes
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
+if applcubicwebversion < (3, 2, 2) and cubicwebversion >= (3, 2, 1):
+   from base64 import b64encode
+   for table in ('entities', 'deleted_entities'):
+      for eid, extid in sql('SELECT eid, extid FROM %s WHERE extid is NOT NULL'
+                            % table, ask_confirm=False):
+         sql('UPDATE %s SET extid=%%(extid)s WHERE eid=%%(eid)s' % table,
+             {'extid': b64encode(extid), 'eid': eid}, ask_confirm=False)
+   checkpoint()
+
 if applcubicwebversion < (3, 2, 0) and cubicwebversion >= (3, 2, 0):
    add_cube('card', update_database=False)
 
@@ -24,7 +34,7 @@
     add_relation_type('is_instance_of')
     # fill the relation using an efficient sql query instead of using rql
     sql('INSERT INTO is_instance_of_relation '
-	'  SELECT * from is_relation')
+        '  SELECT * from is_relation')
     checkpoint()
     session.set_shared_data('do-not-insert-is_instance_of', False)
 
--- a/misc/migration/postcreate.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/misc/migration/postcreate.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""cubicweb post creation script, set user's workflow"""
+"""cubicweb post creation script, set user's workflow
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 activatedeid = add_state(_('activated'), 'CWUser', initial=True)
 deactivatedeid = add_state(_('deactivated'), 'CWUser')
--- a/rset.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/rset.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """The `ResultSet` class which is returned as result of a rql query
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -11,7 +12,7 @@
 from rql import nodes
 
 from cubicweb import NotAnEntity
-    
+
 
 class ResultSet(object):
     """a result set wrap a RQL query result. This object implements a partial
@@ -53,12 +54,12 @@
         self.req = None
         # actions cache
         self._rsetactions = None
-        
+
     def __str__(self):
         if not self.rows:
             return '<empty resultset %s>' % self.rql
         return '<resultset %s (%s rows)>' % (self.rql, len(self.rows))
-    
+
     def __repr__(self):
         if not self.rows:
             return '<empty resultset for %r>' % self.rql
@@ -85,22 +86,22 @@
             actions = self.vreg.possible_vobjects('actions', self.req, self, **kwargs)
             self._rsetactions[key] = actions
             return actions
-    
+
     def __len__(self):
         """returns the result set's size"""
         return self.rowcount
 
     def __nonzero__(self):
         return self.rowcount
-    
+
     def __getitem__(self, i):
         """returns the ith element of the result set"""
         return self.rows[i] #ResultSetRow(self.rows[i])
-    
+
     def __getslice__(self, i, j):
         """returns slice [i:j] of the result set"""
         return self.rows[i:j]
-        
+
     def __iter__(self):
         """Returns an iterator over rows"""
         return iter(self.rows)
@@ -126,7 +127,7 @@
         :param transformcb:
           a callable which should take a row and its type description as
           parameters, and return the transformed row and type description.
-          
+
 
         :type col: int
         :param col: the column index
@@ -199,7 +200,7 @@
 
     def split_rset(self, keyfunc=None, col=0, return_dict=False):
         """Splits the result set in multiple result set according to a given key
-    
+
         :type keyfunc: callable(entity or FinalType)
         :param keyfunc:
           a callable which should take a value of the rset in argument and
@@ -256,7 +257,7 @@
 
         :type offset: int
         :param offset: the offset index
-        
+
         :type inplace: bool
         :param inplace:
           if true, the result set is modified in place, else a new result set
@@ -289,7 +290,7 @@
                 copy_cache(rset, 'get_entity', self)
         rset.limited = (limit, offset)
         return rset
-    
+
     def printable_rql(self, encoded=False):
         """return the result set's origin rql as a string, with arguments
         substitued
@@ -301,11 +302,11 @@
             if isinstance(rqlstr, unicode):
                 return rqlstr
             return unicode(rqlstr, encoding)
-        else: 
+        else:
             if isinstance(rqlstr, unicode):
                 return rqlstr.encode(encoding)
             return rqlstr
-       
+
     # client helper methods ###################################################
 
     def entities(self, col=0):
@@ -320,7 +321,7 @@
     def get_entity(self, row, col=None):
         """special method for query retreiving a single entity, returns a
         partially initialized Entity instance.
-        
+
         WARNING: due to the cache wrapping this function, you should NEVER
                  give row as a named parameter (i.e. rset.get_entity(req, 0)
                  is OK but rset.get_entity(row=0, req=req) isn't
@@ -351,7 +352,7 @@
 
         partially means that only attributes selected in the RQL
         query will be directly assigned to the entity.
-        
+
         :type row,col: int, int
         :param row,col:
           row and col numbers localizing the entity among the result's table
@@ -380,9 +381,6 @@
             pass
         # build entity instance
         etype = self.description[row][col]
-        if etype == 'EUser':
-            import traceback
-            traceback.printstack()
         entity = self.vreg.etype_class(etype)(req, self, row, col)
         entity.set_eid(eid)
         # cache entity
@@ -427,7 +425,7 @@
 
     @cached
     def syntax_tree(self):
-        """get the syntax tree for the source query. 
+        """get the syntax tree for the source query.
 
         :rtype: rql.stmts.Statement
         :return: the RQL syntax tree of the originating query
@@ -441,12 +439,12 @@
         else:
             rqlst = self.vreg.parse(self.req, self.rql, self.args)
         return rqlst
-        
+
     @cached
     def column_types(self, col):
         """return the list of different types in the column with the given col
         index default to 0 (ie the first column)
-        
+
         :type col: int
         :param col: the index of the desired column
 
@@ -483,7 +481,7 @@
         etype = self.description[row][col]
         if self.vreg.schema.eschema(etype).is_final():
             # final type, find a better one to locate the correct subquery
-            # (ambiguous if possible) 
+            # (ambiguous if possible)
             for i in xrange(len(rqlst.children[0].selection)):
                 if i == col:
                     continue
@@ -522,7 +520,7 @@
                 __, rhs = rel.get_variable_parts()
                 return rhs.eval(self.args)
         return None
-        
+
 
 def attr_desc_iterator(rqlst, index=0):
     """return an iterator on a list of 2-uple (index, attr_relation)
--- a/rtags.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/rtags.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """relation tags store
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -24,7 +25,8 @@
     This class associates a single tag to each key.
     """
     _allowed_values = None
-    def __init__(self, initfunc=None, allowed_values=None):
+    def __init__(self, name=None, initfunc=None, allowed_values=None):
+        self._name = name or '<unknown>'
         self._tagdefs = {}
         if allowed_values is not None:
             self._allowed_values = allowed_values
@@ -32,7 +34,7 @@
         register_rtag(self)
 
     def __repr__(self):
-        return repr(self._tagdefs)
+        return '%s: %s' % (self._name, repr(self._tagdefs))
 
     # dict compat
     def __getitem__(self, key):
@@ -95,7 +97,9 @@
         #else:
         stype, rtype, otype, tagged = [str(k) for k in key]
         if self._allowed_values is not None:
-            assert tag in self._allowed_values, '%r is not an allowed tag' % tag
+            assert tag in self._allowed_values, \
+                   '%r is not an allowed tag (should be in %s)' % (
+                tag, self._allowed_values)
         self._tagdefs[(rtype, tagged, stype, otype)] = tag
 
     # rtag runtime api ########################################################
--- a/schema.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/schema.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """classes to define schemas for CubicWeb
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/schemas/Bookmark.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/schemas/Bookmark.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,13 +1,20 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 class Bookmark(MetaUserEntityType):
     """define an entity type, used to build the application schema"""
     title = String(required=True, maxsize=128)
     path  = String(maxsize=512, required=True,
                    description=_("relative url of the bookmarked page"))
-    
+
     bookmarked_by = SubjectRelation('CWUser',
                                     description=_("users using this bookmark"))
-    
+
 
 class bookmarked_by(MetaUserRelationType):
     permissions = {'read':   ('managers', 'users', 'guests',),
--- a/schemas/base.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/schemas/base.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """core CubicWeb schema, but not necessary at bootstrap time
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/schemas/bootstrap.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/schemas/bootstrap.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """core CubicWeb schema necessary for bootstrapping the actual application's schema
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 from cubicweb.schema import format_constraint
@@ -57,7 +58,7 @@
     constrained_by = SubjectRelation('CWConstraint', cardinality='*1', composite='subject')
 
     cardinality = String(maxsize=2, internationalizable=True,
-                         vocabulary=[_('?1'), _('11'), _('??'), _('1?')],
+                         vocabulary=[_('?1'), _('11')],
                          description=_('subject/object cardinality'))
     ordernum = Int(description=('control subject entity\'s relations order'), default=0)
 
--- a/schemas/workflow.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/schemas/workflow.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """workflow related schemas
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 class State(MetaEntityType):
--- a/schemaviewer.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/schemaviewer.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """an helper class to display CubicWeb schema using ureports
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -93,10 +94,10 @@
         return data
 
     def eschema_link_url(self, eschema):
-        return self.req.build_url('eetype/%s?vid=eschema' % eschema)
+        return self.req.build_url('cwetype/%s' % eschema)
 
     def rschema_link_url(self, rschema):
-        return self.req.build_url('ertype/%s?vid=eschema' % rschema)
+        return self.req.build_url('cwrtype/%s' % rschema)
 
     def possible_views(self, etype):
         rset = self.req.etype_rset(etype)
--- a/selectors.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/selectors.py	Wed Jun 03 09:15:20 2009 +0200
@@ -36,10 +36,10 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
-
 __docformat__ = "restructuredtext en"
 
 import logging
@@ -114,7 +114,7 @@
 
 
 def score_interface(cls_or_inst, cls, iface):
-    """Return true if the give object (maybe an instance or class) implements
+    """Return XXX if the give object (maybe an instance or class) implements
     the interface.
     """
     if getattr(iface, '__registry__', None) == 'etypes':
@@ -245,6 +245,7 @@
         if kwargs.get('entity'):
             score = self.score_entity(kwargs['entity'])
         elif row is None:
+            col = col or 0
             for row, rowvalue in enumerate(rset.rows):
                 if rowvalue[col] is None: # outer join
                     continue
@@ -255,6 +256,7 @@
                     return escore
                 score += escore
         else:
+            col = col or 0
             etype = rset.description[row][col]
             if etype is not None: # outer join
                 score = self.score(req, rset, row, col)
@@ -273,9 +275,13 @@
 # very basic selectors ########################################################
 
 class yes(Selector):
-    """return arbitrary score"""
-    def __init__(self, score=1):
+    """return arbitrary score
+
+    default score of 0.5 so any other selector take precedence
+    """
+    def __init__(self, score=0.5):
         self.score = score
+
     def __call__(self, *args, **kwargs):
         return self.score
 
@@ -926,6 +932,8 @@
         except Unauthorized:
             return 0
 
+    def __repr__(self):
+        return u'<rql_condition "%s" at %x>' % (self.rql, id(self))
 
 class but_etype(EntitySelector):
     """accept if the given entity types are not found in the result set.
--- a/server/__init__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/__init__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -4,8 +4,9 @@
 This module contains functions to initialize a new repository.
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/server/checkintegrity.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/checkintegrity.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 is checked.
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -61,7 +62,7 @@
                     yield container
     else:
         yield eschema
-    
+
 def reindex_entities(schema, session):
     """reindex all entities in the repository"""
     # deactivate modification_date hook since we don't want them
@@ -103,7 +104,7 @@
     # restore Entity.check
     Entity.check = _check
 
-    
+
 def check_schema(schema, session, eids, fix=1):
     """check serialized schema"""
     print 'Checking serialized schema'
@@ -122,7 +123,7 @@
                 count, cstrname, en, rn)
 
 
-    
+
 def check_text_index(schema, session, eids, fix=1):
     """check all entities registered in the text index"""
     print 'Checking text index'
@@ -172,8 +173,8 @@
                     print >> sys.stderr, ' [FIXED]'
                 else:
                     print >> sys.stderr
-                
-            
+
+
 def bad_related_msg(rtype, target, eid, fix):
     msg = '  A relation %s with %s eid %s exists but no such entity in sources'
     print >> sys.stderr, msg % (rtype, target, eid),
@@ -181,8 +182,8 @@
         print >> sys.stderr, ' [FIXED]'
     else:
         print >> sys.stderr
-    
-    
+
+
 def check_relations(schema, session, eids, fix=1):
     """check all relations registered in the repo system table"""
     print 'Checking relations'
--- a/server/hookhelper.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/hookhelper.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """helper functions for application hooks
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -42,7 +43,7 @@
     for session in repo._sessions.values():
         if ueid == session.user.eid:
             yield session
-        
+
 
 # mail related ################################################################
 
@@ -58,17 +59,17 @@
         else:
             assert recipients is None
             self.to_send = []
-        super(SendMailOp, self).__init__(session, **kwargs) 
-       
+        super(SendMailOp, self).__init__(session, **kwargs)
+
     def register(self, session):
         previous = super(SendMailOp, self).register(session)
         if previous:
             self.to_send = previous.to_send + self.to_send
-        
+
     def commit_event(self):
         self.repo.threaded_task(self.sendmails)
 
-    def sendmails(self):        
+    def sendmails(self):
         server, port = self.config['smtp-host'], self.config['smtp-port']
         SMTP_LOCK.acquire()
         try:
@@ -89,7 +90,7 @@
             smtp.close()
         finally:
             SMTP_LOCK.release()
-            
+
 
 # state related ###############################################################
 
--- a/server/hooks.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/hooks.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 entities...
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -18,13 +19,13 @@
 
 def relation_deleted(session, eidfrom, rtype, eidto):
     session.add_query_data('pendingrelations', (eidfrom, rtype, eidto))
-    
+
 
 # base meta-data handling #####################################################
 
 def setctime_before_add_entity(session, entity):
     """before create a new entity -> set creation and modification date
- 
+
     this is a conveniency hook, you shouldn't have to disable it
     """
     if not 'creation_date' in entity:
@@ -36,9 +37,9 @@
     """update an entity -> set modification date"""
     if not 'modification_date' in entity:
         entity['modification_date'] = datetime.now()
-        
+
 class SetCreatorOp(PreCommitOperation):
-        
+
     def precommit_event(self):
         if self.eid in self.session.query_data('pendingeids', ()):
             # entity have been created and deleted in the same transaction
@@ -46,7 +47,7 @@
         ueid = self.session.user.eid
         execute = self.session.unsafe_execute
         if not execute('Any X WHERE X created_by U, X eid %(x)s',
-                       {'x': self.eid}, 'x'): 
+                       {'x': self.eid}, 'x'):
             execute('SET X created_by U WHERE X eid %(x)s, U eid %(u)s',
                     {'x': self.eid, 'u': ueid}, 'x')
 
@@ -93,15 +94,15 @@
     if session.repo.schema.rschema(rtype).fulltext_container:
         FTIndexEntityOp(session, entity=session.entity(eidto))
         FTIndexEntityOp(session, entity=session.entity(eidfrom))
-    
+
 class SyncOwnersOp(PreCommitOperation):
-        
+
     def precommit_event(self):
         self.session.unsafe_execute('SET X owned_by U WHERE C owned_by U, C eid %(c)s,'
                                     'NOT EXISTS(X owned_by U, X eid %(x)s)',
                                     {'c': self.compositeeid, 'x': self.composedeid},
                                     ('c', 'x'))
-        
+
 def sync_owner_after_add_composite_relation(session, eidfrom, rtype, eidto):
     """when adding composite relation, the composed should have the same owners
     has the composite
@@ -114,7 +115,7 @@
         SyncOwnersOp(session, compositeeid=eidfrom, composedeid=eidto)
     elif composite == 'object':
         SyncOwnersOp(session, compositeeid=eidto, composedeid=eidfrom)
-    
+
 def _register_metadata_hooks(hm):
     """register meta-data related hooks on the hooks manager"""
     hm.register_hook(setctime_before_add_entity, 'before_add_entity', '')
@@ -127,14 +128,14 @@
         hm.register_hook(setis_after_add_entity, 'after_add_entity', '')
     if 'CWUser' in hm.schema:
         hm.register_hook(setowner_after_add_user, 'after_add_entity', 'CWUser')
-            
+
 # core hooks ##################################################################
-    
+
 class DelayedDeleteOp(PreCommitOperation):
     """delete the object of composite relation except if the relation
     has actually been redirected to another composite
     """
-        
+
     def precommit_event(self):
         session = self.session
         if not self.eid in session.query_data('pendingeids', ()):
@@ -142,7 +143,7 @@
             session.unsafe_execute('DELETE %s X WHERE X eid %%(x)s, NOT %s'
                                    % (etype, self.relation),
                                    {'x': self.eid}, 'x')
-    
+
 def handle_composite_before_del_relation(session, eidfrom, rtype, eidto):
     """delete the object of composite relation"""
     composite = rproperty(session, rtype, eidfrom, eidto, 'composite')
@@ -157,7 +158,7 @@
 
 
 # schema validation hooks #####################################################
-        
+
 class CheckConstraintsOperation(LateOperation):
     """check a new relation satisfy its constraints
     """
@@ -176,10 +177,10 @@
             except NotImplementedError:
                 self.critical('can\'t check constraint %s, not supported',
                               constraint)
-    
+
     def commit_event(self):
         pass
-    
+
 def cstrcheck_after_add_relation(session, eidfrom, rtype, eidto):
     """check the relation satisfy its constraints
 
@@ -213,7 +214,7 @@
     case the relation is being replaced
     """
     eid, rtype = None, None
-    
+
     def precommit_event(self):
         # recheck pending eids
         if self.eid in self.session.query_data('pendingeids', ()):
@@ -224,18 +225,18 @@
             raise ValidationError(self.eid, {self.rtype: msg % {'rtype': self.rtype,
                                                                 'etype': etype,
                                                                 'eid': self.eid}})
-    
+
     def commit_event(self):
         pass
-        
+
     def _rql(self):
         raise NotImplementedError()
-    
+
 class CheckSRelationOp(CheckRequiredRelationOperation):
     """check required subject relation"""
     def _rql(self):
         return 'Any O WHERE S eid %%(x)s, S %s O' % self.rtype, {'x': self.eid}, 'x'
-    
+
 class CheckORelationOp(CheckRequiredRelationOperation):
     """check required object relation"""
     def _rql(self):
@@ -248,7 +249,7 @@
             break
     else:
         opcls(session, rtype=rtype, eid=eid)
-    
+
 def cardinalitycheck_after_add_entity(session, entity):
     """check cardinalities are satisfied"""
     eid = entity.eid
@@ -283,7 +284,7 @@
 def _register_core_hooks(hm):
     hm.register_hook(handle_composite_before_del_relation, 'before_delete_relation', '')
     hm.register_hook(before_del_group, 'before_delete_entity', 'CWGroup')
-    
+
     #hm.register_hook(cstrcheck_before_update_entity, 'before_update_entity', '')
     hm.register_hook(cardinalitycheck_after_add_entity, 'after_add_entity', '')
     hm.register_hook(cardinalitycheck_before_del_relation, 'before_delete_relation', '')
@@ -293,13 +294,13 @@
 
 
 # user/groups synchronisation #################################################
-            
+
 class GroupOperation(Operation):
     """base class for group operation"""
     geid = None
     def __init__(self, session, *args, **kwargs):
         """override to get the group name before actual groups manipulation:
-        
+
         we may temporarily loose right access during a commit event, so
         no query should be emitted while comitting
         """
@@ -318,13 +319,13 @@
         except KeyError:
             self.error('user %s not in group %s',  self.cnxuser, self.group)
             return
-    
+
 def after_del_in_group(session, fromeid, rtype, toeid):
     """modify user permission, need to update users"""
     for session_ in get_user_sessions(session.repo, fromeid):
         DeleteGroupOp(session, cnxuser=session_.user, geid=toeid)
 
-        
+
 class AddGroupOp(GroupOperation):
     """synchronize user when a in_group relation has been added"""
     def commit_event(self):
@@ -347,7 +348,7 @@
     def __init__(self, session, cnxid):
         self.cnxid = cnxid
         Operation.__init__(self, session)
-        
+
     def commit_event(self):
         """the observed connections pool has been commited"""
         try:
@@ -359,7 +360,7 @@
     """modify user permission, need to update users"""
     for session_ in get_user_sessions(session.repo, eid):
         DelUserOp(session, session_.id)
-    
+
 def _register_usergroup_hooks(hm):
     """register user/group related hooks on the hooks manager"""
     hm.register_hook(after_del_user, 'after_delete_entity', 'CWUser')
@@ -430,7 +431,7 @@
 
 def set_initial_state_after_add(session, entity):
     SetInitialStateOp(session, entity=entity)
-    
+
 def _register_wf_hooks(hm):
     """register workflow related hooks on the hooks manager"""
     if 'in_state' in hm.schema:
@@ -447,7 +448,7 @@
 
 class DelCWPropertyOp(Operation):
     """a user's custom properties has been deleted"""
-    
+
     def commit_event(self):
         """the observed connections pool has been commited"""
         try:
@@ -457,14 +458,14 @@
 
 class ChangeCWPropertyOp(Operation):
     """a user's custom properties has been added/changed"""
-        
+
     def commit_event(self):
         """the observed connections pool has been commited"""
         self.epropdict[self.key] = self.value
 
 class AddCWPropertyOp(Operation):
     """a user's custom properties has been added/changed"""
-        
+
     def commit_event(self):
         """the observed connections pool has been commited"""
         eprop = self.eprop
@@ -485,7 +486,7 @@
                                {'x': entity.eid, 'u': session.user.eid}, 'x')
     else:
         AddCWPropertyOp(session, eprop=entity)
-        
+
 def after_update_eproperty(session, entity):
     key, value = entity.pkey, entity.value
     try:
@@ -502,7 +503,7 @@
         # site wide properties
         ChangeCWPropertyOp(session, epropdict=session.vreg.eprop_values,
                           key=key, value=value)
-        
+
 def before_del_eproperty(session, eid):
     for eidfrom, rtype, eidto in session.query_data('pendingrelations', ()):
         if rtype == 'for_user' and eidfrom == eid:
@@ -524,7 +525,7 @@
     for session_ in get_user_sessions(session.repo, toeid):
         ChangeCWPropertyOp(session, epropdict=session_.user.properties,
                           key=key, value=value)
-        
+
 def before_del_for_user(session, fromeid, rtype, toeid):
     key = session.execute('Any K WHERE P eid %(x)s, P pkey K',
                           {'x': fromeid}, 'x')[0][0]
--- a/server/hooksmanager.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/hooksmanager.py	Wed Jun 03 09:15:20 2009 +0200
@@ -23,12 +23,13 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
-ENTITIES_HOOKS = ('before_add_entity',    'after_add_entity', 
+ENTITIES_HOOKS = ('before_add_entity',    'after_add_entity',
                   'before_update_entity', 'after_update_entity',
                   'before_delete_entity', 'after_delete_entity')
 RELATIONS_HOOKS = ('before_add_relation',   'after_add_relation' ,
@@ -42,7 +43,7 @@
     """handle hooks registration and calls
     """
     verification_hooks_activated = True
-    
+
     def __init__(self, schema):
         self.set_schema(schema)
 
@@ -50,20 +51,20 @@
         self._hooks = {}
         self.schema = schema
         self._init_hooks(schema)
-        
+
     def register_hooks(self, hooks):
         """register a dictionary of hooks :
-        
+
              {'event': {'entity or relation type': [callbacks list]}}
         """
         for event, subevents in hooks.items():
             for subevent, callbacks in subevents.items():
                 for callback in callbacks:
                     self.register_hook(callback, event, subevent)
-                    
+
     def register_hook(self, function, event, etype=''):
         """register a function to call when <event> occurs
-        
+
          <etype> is an entity/relation type or an empty string.
          If etype is the empty string, the function will be called at each
          event, else the function will be called only when event occurs on an
@@ -76,14 +77,14 @@
             self._hooks[event][etype].append(function)
             self.debug('registered hook %s on %s (%s)', event, etype or 'any',
                        function.func_name)
-            
+
         except KeyError:
             self.error('can\'t register hook %s on %s (%s)',
                        event, etype or 'any', function.func_name)
-            
+
     def unregister_hook(self, function, event, etype=''):
         """register a function to call when <event> occurs
-        
+
         <etype> is an entity/relation type or an empty string.
         If etype is the empty string, the function will be called at each
         event, else the function will be called only when event occurs on an
@@ -109,7 +110,7 @@
             for hook in self._hooks[__event][__type]:
                 #print '[%s]'%__type, hook.__name__
                 hook(*args, **kwargs)
-    
+
     def _init_hooks(self, schema):
         """initialize the hooks map"""
         for hook_event in ENTITIES_HOOKS:
@@ -164,7 +165,7 @@
         self.unregister_hook(uniquecstrcheck_before_modification, 'before_update_entity', '')
 #         self.unregister_hook(tidy_html_fields('before_add_entity'), 'before_add_entity', '')
 #         self.unregister_hook(tidy_html_fields('before_update_entity'), 'before_update_entity', '')
-        
+
     def reactivate_verification_hooks(self):
         from cubicweb.server.hooks import (cardinalitycheck_after_add_entity,
                                         cardinalitycheck_before_del_relation,
@@ -179,7 +180,7 @@
         self.register_hook(uniquecstrcheck_before_modification, 'before_update_entity', '')
 #         self.register_hook(tidy_html_fields('before_add_entity'), 'before_add_entity', '')
 #         self.register_hook(tidy_html_fields('before_update_entity'), 'before_update_entity', '')
-            
+
 from cubicweb.selectors import yes
 from cubicweb.appobject import AppObject
 
@@ -199,16 +200,16 @@
     events = None
     accepts = None
     enabled = True
-    
+
     def __init__(self, event=None):
         super(Hook, self).__init__()
         self.event = event
-        
+
     @classmethod
     def registered(cls, vreg):
         super(Hook, cls).registered(vreg)
         return cls()
-    
+
     @classmethod
     def register_to(cls):
         if not cls.enabled:
@@ -232,7 +233,7 @@
                             continue
                         yield event, str(eetype)
                         done.add((event, eetype))
-                        
+
 
     def make_callback(self, event):
         if len(self.events) == 1:
@@ -241,7 +242,7 @@
 
     def call(self):
         raise NotImplementedError
-    
+
 class SystemHook(Hook):
     accepts = ('',)
 
--- a/server/migractions.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/migractions.py	Wed Jun 03 09:15:20 2009 +0200
@@ -11,8 +11,9 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/server/msplanner.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/msplanner.py	Wed Jun 03 09:15:20 2009 +0200
@@ -70,8 +70,9 @@
 
 
 :organization: Logilab
-:copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/server/mssteps.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/mssteps.py	Wed Jun 03 09:15:20 2009 +0200
@@ -6,8 +6,9 @@
   for now)
 
 :organization: Logilab
-:copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -16,7 +17,7 @@
 from cubicweb.server.ssplanner import (LimitOffsetMixIn, Step, OneFetchStep,
                                     varmap_test_repr, offset_result)
 
-AGGR_TRANSFORMS = {'COUNT':'SUM', 'MIN':'MIN', 'MAX':'MAX', 'SUM': 'SUM'} 
+AGGR_TRANSFORMS = {'COUNT':'SUM', 'MIN':'MIN', 'MAX':'MAX', 'SUM': 'SUM'}
 
 def remove_clauses(union, keepgroup):
     clauses = []
@@ -73,7 +74,7 @@
                         if lhsvar.name in srqlst.defined_vars:
                             key = '%s.%s' % (lhsvar.name, rel.r_type)
                             self.outputmap[key] = self.outputmap[var.name]
-                
+
     def execute(self):
         """execute this step"""
         self.execute_children()
@@ -86,7 +87,7 @@
             source.flying_insert(self.table, plan.session, union, plan.args,
                                  self.inputmap)
         restore_clauses(union, self.keepgroup, clauses)
-            
+
     def mytest_repr(self):
         """return a representation of this step suitable for test"""
         clauses = remove_clauses(self.union, self.keepgroup)
@@ -104,7 +105,7 @@
         finally:
             restore_clauses(self.union, self.keepgroup, clauses)
 
-    
+
 class AggrStep(LimitOffsetMixIn, Step):
     """step consisting in making aggregat from temporary data in the system
     source
@@ -123,7 +124,7 @@
             plan.init_temp_table(outputtable, selection, select.solutions[0])
 
         #self.inputmap = inputmap
-        
+
     def mytest_repr(self):
         """return a representation of this step suitable for test"""
         sel = self.select.selection
@@ -167,7 +168,7 @@
                     clause.append(term.accept(self))
                     # restaure the tree XXX necessary?
                     term.name = orig_name
-                    term.children = orig_children                
+                    term.children = orig_children
                 except KeyError:
                     clause.append(var_name)
             else:
@@ -215,28 +216,28 @@
             self.plan.create_temp_table(self.outputtable)
             sql = 'INSERT INTO %s %s' % (self.outputtable, sql)
         return self.plan.sqlexec(sql, self.plan.args)
-    
+
     def visit_function(self, function):
         """generate SQL name for a function"""
         return '%s(%s)' % (function.name,
                            ','.join(c.accept(self) for c in function.children))
-        
+
     def visit_variableref(self, variableref):
         """get the sql name for a variable reference"""
         try:
             return self.inputmap[variableref.name]
         except KeyError: # XXX duh? explain
             return variableref.variable.name
-        
+
     def visit_constant(self, constant):
         """generate SQL name for a constant"""
         assert constant.type == 'Int'
         return str(constant.value)
-    
+
 
 class UnionStep(LimitOffsetMixIn, Step):
     """union results of child in-memory steps (e.g. OneFetchStep / AggrStep)"""
-        
+
     def execute(self):
         """execute this step"""
         result = []
@@ -258,7 +259,7 @@
                 if len(result) >= olimit:
                     return result[:olimit]
         return result
-        
+
     def mytest_repr(self):
         """return a representation of this step suitable for test"""
         return (self.__class__.__name__, self.limit, self.offset)
@@ -266,7 +267,7 @@
 
 class IntersectStep(UnionStep):
     """return intersection of results of child in-memory steps (e.g. OneFetchStep / AggrStep)"""
-        
+
     def execute(self):
         """execute this step"""
         result = set()
--- a/server/pool.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/pool.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,7 +2,7 @@
 
 * the rql repository has a limited number of connections pools, each of them
   dealing with a set of connections on each source used by the repository
-  
+
 * operation may be registered by hooks during a transaction, which will  be
   fired when the pool is commited or rollbacked
 
@@ -11,13 +11,14 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
 import sys
-    
+
 class ConnectionsPool(object):
     """handle connections on a set of sources, at some point associated to a
     user session
@@ -40,7 +41,7 @@
         for source, cnx in self.source_cnxs.values():
             # let exception propagates
             cnx.commit()
-        
+
     def rollback(self):
         """rollback the current transaction for this user"""
         for source, cnx in self.source_cnxs.values():
@@ -64,7 +65,7 @@
                 cnx.close()
             except:
                 continue
-            
+
     # internals ###############################################################
 
     def pool_set(self, session):
@@ -75,7 +76,7 @@
         """pool is being reseted"""
         for source, cnx in self.source_cnxs.values():
             source.pool_reset(cnx)
-        
+
     def __getitem__(self, uri):
         """subscription notation provide access to sources'cursors"""
         try:
@@ -86,7 +87,7 @@
                 # None possible on sources without cursor support such as ldap
                 self._cursors[uri] = cursor
         return cursor
-    
+
     def sources(self):
         """return the source objects handled by this pool"""
         # implementation details of flying insert requires the system source
@@ -97,11 +98,11 @@
                 continue
             yield source
         #return [source_cnx[0] for source_cnx in self.source_cnxs.values()]
-    
+
     def source(self, uid):
         """return the source object with the given uri"""
         return self.source_cnxs[uid][0]
-    
+
     def connection(self, uid):
         """return the connection on the source object with the given uri"""
         return self.source_cnxs[uid][1]
@@ -109,7 +110,7 @@
     def reconnect(self, source):
         """reopen a connection for this source"""
         source.info('trying to reconnect')
-        self.source_cnxs[source.uri] = (source, source.get_connection())        
+        self.source_cnxs[source.uri] = (source, source.get_connection())
         del self._cursors[source.uri]
 
     def check_connections(self):
@@ -133,11 +134,11 @@
       do any heavy computation or raise an exception if the commit can't go.
       You can add some new operation during this phase but their precommit
       event won't be triggered
-      
+
     commit:
       the pool is preparing to commit. You should avoid to do to expensive
       stuff or something that may cause an exception in this event
-      
+
     revertcommit:
       if an operation failed while commited, this event is triggered for
       all operations which had their commit event already to let them
@@ -153,7 +154,7 @@
     order of operations may be important, and is controlled according to:
     * operation's class
     """
-    
+
     def __init__(self, session, **kwargs):
         self.session = session
         self.user = session.user
@@ -165,10 +166,10 @@
         # execution information
         self.processed = None # 'precommit', 'commit'
         self.failed = False
-        
+
     def register(self, session):
         session.add_operation(self, self.insert_index())
-        
+
     def insert_index(self):
         """return the index of  the lastest instance which is not a
         LateOperation instance
@@ -177,17 +178,17 @@
             if isinstance(op, (LateOperation, SingleLastOperation)):
                 return i
         return None
-    
+
     def handle_event(self, event):
         """delegate event handling to the opertaion"""
         getattr(self, event)()
 
     def precommit_event(self):
         """the observed connections pool is preparing a commit"""
-    
+
     def revertprecommit_event(self):
         """an error went when pre-commiting this operation or a later one
-        
+
         should revert pre-commit's changes but take care, they may have not
         been all considered if it's this operation which failed
         """
@@ -195,17 +196,17 @@
     def commit_event(self):
         """the observed connections pool is commiting"""
         raise NotImplementedError()
-    
+
     def revertcommit_event(self):
         """an error went when commiting this operation or a later one
-        
+
         should revert commit's changes but take care, they may have not
         been all considered if it's this operation which failed
         """
-    
+
     def rollback_event(self):
         """the observed connections pool has been rollbacked
-        
+
         do nothing by default, the operation will just be removed from the pool
         operation list
         """
@@ -226,7 +227,7 @@
 class LateOperation(Operation):
     """special operation which should be called after all possible (ie non late)
     operations
-    """    
+    """
     def insert_index(self):
         """return the index of  the lastest instance which is not a
         SingleLastOperation instance
@@ -238,7 +239,7 @@
 
 
 class SingleOperation(Operation):
-    """special operation which should be called once"""    
+    """special operation which should be called once"""
     def register(self, session):
         """override register to handle cases where this operation has already
         been added
@@ -251,7 +252,7 @@
             equivalent = None
         session.add_operation(self, self.insert_index())
         return equivalent
-    
+
     def equivalent_index(self, operations):
         """return the index of the equivalent operation if any"""
         equivalents = [i for i, op in enumerate(operations)
@@ -264,7 +265,7 @@
 class SingleLastOperation(SingleOperation):
     """special operation which should be called once and after all other
     operations
-    """    
+    """
     def insert_index(self):
         return None
 
--- a/server/querier.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/querier.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 security checking and data aggregation.
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -84,7 +85,7 @@
             #assert len(erqlexprs) == 1
             localchecks[varname] = tuple(erqlexprs)
     return localchecks
-                    
+
 def noinvariant_vars(restricted, select, nbtrees):
     # a variable can actually be invariant if it has not been restricted for
     # security reason or if security assertion hasn't modified the possible
@@ -114,12 +115,12 @@
                 colalias = newselect.get_variable(vref.name, len(aliases))
                 aliases.append(VariableRef(colalias))
                 selected.add(vref.name)
-                
+
 # Plans #######################################################################
 
 class ExecutionPlan(object):
     """the execution model of a rql query, composed of querier steps"""
-    
+
     def __init__(self, querier, rqlst, args, session):
         # original rql syntax tree
         self.rqlst = rqlst
@@ -137,11 +138,11 @@
         self.schema = querier.schema
         self.rqlhelper = querier._rqlhelper
         self.sqlannotate = querier.sqlgen_annotate
-        
+
     def annotate_rqlst(self):
         if not self.rqlst.annotated:
             self.rqlhelper.annotate(self.rqlst)
-            
+
     def add_step(self, step):
         """add a step to the plan"""
         self.steps.append(step)
@@ -149,10 +150,10 @@
     def clean(self):
         """remove temporary tables"""
         self.syssource.clean_temp_data(self.session, self.temp_tables)
-        
+
     def sqlexec(self, sql, args=None):
         return self.syssource.sqlexec(self.session, sql, args)
-            
+
     def execute(self):
         """execute a plan and return resulting rows"""
         try:
@@ -162,7 +163,7 @@
             return result
         finally:
             self.clean()
-            
+
     def init_temp_table(self, table, selected, sol):
         """initialize sql schema and variable map for a temporary table which
         will be used to store result for the given rqlst
@@ -175,17 +176,17 @@
                                                                  table)
             self.temp_tables[table] = [outputmap, sqlschema, False]
         return outputmap
-        
+
     def create_temp_table(self, table):
         """create a temporary table to store result for the given rqlst"""
         if not self.temp_tables[table][-1]:
             sqlschema = self.temp_tables[table][1]
             self.syssource.create_temp_table(self.session, table, sqlschema)
             self.temp_tables[table][-1] = True
-        
+
     def preprocess(self, union, security=True):
         """insert security when necessary then annotate rql st for sql generation
-        
+
         return rqlst to actually execute
         """
         #if server.DEBUG:
@@ -279,7 +280,7 @@
         are removed, else if the user may read it (eg if an rql expression is
         defined for the "read" permission of the related type), the local checks
         dict for the solution is updated
-        
+
         return a dict with entries for each different local check necessary,
         with associated solutions as value. A local check is defined by a list
         of 2-uple, with variable name as first item and the necessary rql
@@ -346,11 +347,11 @@
         self.rqlhelper.annotate(rqlst)
         self.preprocess(rqlst, security=False)
         return rqlst
-       
+
 class InsertPlan(ExecutionPlan):
     """an execution model specific to the INSERT rql query
     """
-    
+
     def __init__(self, querier, rqlst, args, session):
         ExecutionPlan.__init__(self, querier, rqlst, args, session)
         # save originaly selected variable, we may modify this
@@ -387,7 +388,7 @@
                     value = rhs.eval(self.args)
                     eschema = edef.e_schema
                     attrtype = eschema.subject_relation(rtype).objects(eschema)[0]
-                    if attrtype == 'Password' and isinstance(value, unicode): 
+                    if attrtype == 'Password' and isinstance(value, unicode):
                         value = value.encode('UTF8')
                     edef[rtype] = value
                 elif to_build.has_key(str(rhs)):
@@ -397,12 +398,12 @@
                     to_select.setdefault(edef, []).append( (rtype, rhs, 0) )
         return to_select
 
-        
+
     def add_entity_def(self, edef):
         """add an entity definition to build"""
         edef.querier_pending_relations = {}
         self.e_defs[-1].append(edef)
-        
+
     def add_relation_def(self, rdef):
         """add an relation definition to build"""
         self.r_defs.append(rdef)
@@ -410,11 +411,11 @@
             self._r_subj_index.setdefault(rdef[0], []).append(rdef)
         if not isinstance(rdef[2], int):
             self._r_obj_index.setdefault(rdef[2], []).append(rdef)
-        
+
     def substitute_entity_def(self, edef, edefs):
         """substitute an incomplete entity definition by a list of complete
         equivalents
-        
+
         e.g. on queries such as ::
           INSERT Personne X, Societe Y: X nom N, Y nom 'toto', X travaille Y
           WHERE U login 'admin', U login N
@@ -455,7 +456,7 @@
                     for edef in edefs:
                         result.append( (exp_rdef[0], exp_rdef[1], edef) )
                 self._expanded_r_defs[rdef] = result
-        
+
     def _expanded(self, rdef):
         """return expanded value for the given relation definition"""
         try:
@@ -463,7 +464,7 @@
         except KeyError:
             self.r_defs.remove(rdef)
             return [rdef]
-        
+
     def relation_defs(self):
         """return the list for relation definitions to insert"""
         for rdefs in self._expanded_r_defs.values():
@@ -471,11 +472,11 @@
                 yield rdef
         for rdef in self.r_defs:
             yield rdef
-            
+
     def insert_entity_defs(self):
         """return eids of inserted entities in a suitable form for the resulting
         result set, e.g.:
-        
+
         e.g. on queries such as ::
           INSERT Personne X, Societe Y: X nom N, Y nom 'toto', X travaille Y
           WHERE U login 'admin', U login N
@@ -490,7 +491,7 @@
             results.append([repo.glob_add_entity(session, edef)
                             for edef in row])
         return results
-        
+
     def insert_relation_defs(self):
         session = self.session
         repo = session.repo
@@ -514,18 +515,18 @@
 
 class QuerierHelper(object):
     """helper class to execute rql queries, putting all things together"""
-    
+
     def __init__(self, repo, schema):
         # system info helper
         self._repo = repo
         # application schema
         self.set_schema(schema)
-        
+
     def set_schema(self, schema):
         self.schema = schema
         # rql parsing / analysing helper
         self._rqlhelper = RQLHelper(schema, special_relations={'eid': 'uid',
-                                                               'has_text': 'fti'})        
+                                                               'has_text': 'fti'})
         self._rql_cache = Cache(self._repo.config['rql-cache-size'])
         self.cache_hit, self.cache_miss = 0, 0
         # rql planner
@@ -535,11 +536,11 @@
             from cubicweb.server.ssplanner import SSPlanner
             self._planner = SSPlanner(schema, self._rqlhelper)
         else:
-            from cubicweb.server.msplanner import MSPlanner            
+            from cubicweb.server.msplanner import MSPlanner
             self._planner = MSPlanner(schema, self._rqlhelper)
         # sql generation annotator
         self.sqlgen_annotate = SQLGenAnnotator(schema).annotate
-        
+
     def parse(self, rql, annotate=False):
         """return a rql syntax tree for the given rql"""
         try:
@@ -559,7 +560,7 @@
         if rqlst.TYPE == 'insert':
             return InsertPlan(self, rqlst, args, session)
         return ExecutionPlan(self, rqlst, args, session)
-        
+
     def execute(self, session, rql, args=None, eid_key=None, build_descr=True):
         """execute a rql query, return resulting rows and their description in
         a `ResultSet` object
@@ -578,7 +579,7 @@
 
         on INSERT queries, there will be on row with the eid of each inserted
         entity
-        
+
         result for DELETE and SET queries is undefined yet
 
         to maximize the rql parsing/analyzing cache performance, you should
@@ -599,7 +600,8 @@
                 except KeyError:
                     raise QueryError('bad cache key %s (no value)' % key)
                 except TypeError:
-                    raise QueryError('bad cache key %s (value: %r)' % (key, args[key]))
+                    raise QueryError('bad cache key %s (value: %r)' % (
+                        key, args[key]))
                 except UnknownEid:
                     # we want queries such as "Any X WHERE X eid 9999"
                     # return an empty result instead of raising UnknownEid
--- a/server/repository.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/repository.py	Wed Jun 03 09:15:20 2009 +0200
@@ -11,8 +11,9 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -217,6 +218,7 @@
         self._get_pool().close(True)
         for i in xrange(config['connections-pool-size']):
             self._available_pools.put_nowait(ConnectionsPool(self.sources))
+        self._shutting_down = False
 
     # internals ###############################################################
 
@@ -349,6 +351,7 @@
         """called on server stop event to properly close opened sessions and
         connections
         """
+        self._shutting_down = True
         if isinstance(self._looping_tasks, tuple): # if tasks have been started
             for looptask in self._looping_tasks:
                 self.info('canceling task %s...', looptask.name)
@@ -618,7 +621,7 @@
         """commit transaction for the session with the given id"""
         self.debug('begin commit for session %s', sessionid)
         try:
-            self._get_session(sessionid, setpool=True).commit()
+            self._get_session(sessionid).commit()
         except (ValidationError, Unauthorized):
             raise
         except:
@@ -629,14 +632,15 @@
         """commit transaction for the session with the given id"""
         self.debug('begin rollback for session %s', sessionid)
         try:
-            self._get_session(sessionid, setpool=True).rollback()
+            self._get_session(sessionid).rollback()
         except:
             self.exception('unexpected error')
             raise
 
-    def close(self, sessionid):
+    def close(self, sessionid, checkshuttingdown=True):
         """close the session with the given id"""
-        session = self._get_session(sessionid, setpool=True)
+        session = self._get_session(sessionid, setpool=True,
+                                    checkshuttingdown=checkshuttingdown)
         # operation uncommited before close are rollbacked before hook is called
         session.rollback()
         self.hm.call_hooks('session_close', session=session)
@@ -689,7 +693,7 @@
         """close every opened sessions"""
         for sessionid in self._sessions.keys():
             try:
-                self.close(sessionid)
+                self.close(sessionid, checkshuttingdown=False)
             except:
                 self.exception('error while closing session %s' % sessionid)
 
@@ -718,8 +722,10 @@
         session.set_pool()
         return session
 
-    def _get_session(self, sessionid, setpool=False):
+    def _get_session(self, sessionid, setpool=False, checkshuttingdown=True):
         """return the user associated to the given session identifier"""
+        if checkshuttingdown and self._shutting_down:
+            raise Exception('Repository is shutting down')
         try:
             session = self._sessions[sessionid]
         except KeyError:
@@ -789,10 +795,10 @@
             raise UnknownEid(eid)
         return extid
 
-    def extid2eid(self, source, lid, etype, session=None, insert=True,
+    def extid2eid(self, source, extid, etype, session=None, insert=True,
                   recreate=False):
         """get eid from a local id. An eid is attributed if no record is found"""
-        cachekey = (str(lid), source.uri)
+        cachekey = (extid, source.uri)
         try:
             return self._extid_cache[cachekey]
         except KeyError:
@@ -801,17 +807,17 @@
         if session is None:
             session = self.internal_session()
             reset_pool = True
-        eid = self.system_source.extid2eid(session, source, lid)
+        eid = self.system_source.extid2eid(session, source, extid)
         if eid is not None:
             self._extid_cache[cachekey] = eid
-            self._type_source_cache[eid] = (etype, source.uri, lid)
+            self._type_source_cache[eid] = (etype, source.uri, extid)
             if recreate:
-                entity = source.before_entity_insertion(session, lid, etype, eid)
+                entity = source.before_entity_insertion(session, extid, etype, eid)
                 entity._cw_recreating = True
                 if source.should_call_hooks:
                     self.hm.call_hooks('before_add_entity', etype, session, entity)
                 # XXX add fti op ?
-                source.after_entity_insertion(session, lid, entity)
+                source.after_entity_insertion(session, extid, entity)
                 if source.should_call_hooks:
                     self.hm.call_hooks('after_add_entity', etype, session, entity)
             if reset_pool:
@@ -819,7 +825,7 @@
             return eid
         if not insert:
             return
-        # no link between lid and eid, create one using an internal session
+        # no link between extid and eid, create one using an internal session
         # since the current session user may not have required permissions to
         # do necessary stuff and we don't want to commit user session.
         #
@@ -831,13 +837,13 @@
         try:
             eid = self.system_source.create_eid(session)
             self._extid_cache[cachekey] = eid
-            self._type_source_cache[eid] = (etype, source.uri, lid)
-            entity = source.before_entity_insertion(session, lid, etype, eid)
+            self._type_source_cache[eid] = (etype, source.uri, extid)
+            entity = source.before_entity_insertion(session, extid, etype, eid)
             if source.should_call_hooks:
                 self.hm.call_hooks('before_add_entity', etype, session, entity)
             # XXX call add_info with complete=False ?
-            self.add_info(session, entity, source, lid)
-            source.after_entity_insertion(session, lid, entity)
+            self.add_info(session, entity, source, extid)
+            source.after_entity_insertion(session, extid, entity)
             if source.should_call_hooks:
                 self.hm.call_hooks('after_add_entity', etype, session, entity)
             else:
--- a/server/rqlannotation.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/rqlannotation.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 code generation.
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -69,7 +70,7 @@
             # "Any X", "Any X, Y WHERE X attr Y"
             stinfo['invariant'] = False
             continue
-        joins = set()            
+        joins = set()
         invariant = False
         for ref in var.references():
             rel = ref.relation()
@@ -78,7 +79,7 @@
             lhs, rhs = rel.get_parts()
             onlhs = ref is lhs
             if rel.r_type == 'eid':
-                if not (onlhs and len(stinfo['relations']) > 1): 
+                if not (onlhs and len(stinfo['relations']) > 1):
                     break
                 if not stinfo['constnode']:
                     joins.add(rel)
@@ -110,9 +111,9 @@
                 continue
             if not stinfo['constnode']:
                 if rschema.inlined and rel.neged(strict=True):
-                    # if relation is inlined, can't be invariant if that 
+                    # if relation is inlined, can't be invariant if that
                     # variable is used anywhere else.
-                    # see 'Any P WHERE NOT N ecrit_par P, N eid 512':                    
+                    # see 'Any P WHERE NOT N ecrit_par P, N eid 512':
                     # sql for 'NOT N ecrit_par P' is 'N.ecrit_par is NULL' so P
                     # can use N.ecrit_par as principal
                     if (stinfo['selected'] or len(stinfo['relations']) > 1):
@@ -184,7 +185,7 @@
         return iter(_sort(diffscope_rels)).next()
     # XXX  could use a relation for a different scope if it can't generate
     # duplicates, so we would have to check cardinality
-    raise CantSelectPrincipal()    
+    raise CantSelectPrincipal()
 
 def _select_main_var(relations):
     """given a list of rqlst relations, select one which will be used as main
@@ -265,12 +266,12 @@
             return False
         try:
             data = var.stmt._deamb_data
-        except AttributeError: 
+        except AttributeError:
             data = var.stmt._deamb_data = IsAmbData(self.schema, self.nfdomain)
             data.compute(var.stmt)
         return data.is_ambiguous(var)
 
-        
+
 class IsAmbData(object):
     def __init__(self, schema, nfdomain):
         self.schema = schema
@@ -288,7 +289,7 @@
         self.deambification_map = {}
         # not invariant variables (access to final.inlined relation)
         self.not_invariants = set()
-        
+
     def is_ambiguous(self, var):
         return var in self.ambiguousvars
 
@@ -296,7 +297,7 @@
         self.varsols[var] &= restricted_domain
         if var in self.ambiguousvars and self.varsols[var] == var.stinfo['possibletypes']:
             self.ambiguousvars.remove(var)
-    
+
     def compute(self, rqlst):
         # set domains for each variable
         for varname, var in rqlst.defined_vars.iteritems():
@@ -334,7 +335,7 @@
                 except KeyError:
                     # no relation to deambiguify
                     continue
-        
+
     def _debug_print(self):
         print 'varsols', dict((x, sorted(str(v) for v in values))
                                for x, values in self.varsols.iteritems())
@@ -350,7 +351,7 @@
                     self.maydeambrels[var].add(rel)
                 except KeyError:
                     self.maydeambrels[var] = set((rel,))
-        
+
     def deambiguifying_relation(self, var, rel):
         lhs, rhs = rel.get_variable_parts()
         onlhs = var is getattr(lhs, 'variable', None)
--- a/server/rqlrewrite.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/rqlrewrite.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """RQL rewriting utilities, used for read security checking
 
 :organization: Logilab
-:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 from rql import nodes, stmts, TypeResolverException
@@ -34,7 +35,7 @@
     return newsolutions
 
 class Unsupported(Exception): pass
-        
+
 class RQLRewriter(object):
     """insert some rql snippets into another rql syntax tree"""
     def __init__(self, querier, session):
@@ -51,7 +52,7 @@
             raise Unsupported()
         if len(self.select.solutions) < len(self.solutions):
             raise Unsupported()
-        
+
     def rewrite(self, select, snippets, solutions, kwargs):
         if server.DEBUG:
             print '---- rewrite', select, snippets, solutions
@@ -112,7 +113,7 @@
         add_types_restriction(self.schema, select)
         if server.DEBUG:
             print '---- rewriten', select
-            
+
     def build_variantes(self, newsolutions):
         variantes = set()
         for sol in newsolutions:
@@ -133,7 +134,7 @@
                 for variante in variantes:
                     del variante[(erqlexpr, mainvar, oldvar)]
         return variantes
-    
+
     def insert_snippets(self, snippets, varexistsmap=None):
         self.rewritten = {}
         for varname, erqlexprs in snippets:
@@ -175,7 +176,7 @@
             if varexistsmap is None and not inserted:
                 # no rql expression found matching rql solutions. User has no access right
                 raise Unauthorized()
-            
+
     def insert_snippet(self, varname, snippetrqlst, parent=None):
         new = snippetrqlst.where.accept(self)
         if new is not None:
@@ -240,7 +241,7 @@
                     else:
                         parent.parent.replace(or_, or_.children[0])
                         self._cleanup_inserted(new)
-                    raise 
+                    raise
             return new
 
     def _cleanup_inserted(self, node):
@@ -250,7 +251,7 @@
             if not vref.variable.stinfo['references']:
                 # no more references, undefine the variable
                 del self.select.defined_vars[vref.name]
-        
+
     def _visit_binary(self, node, cls):
         newnode = cls()
         for c in node.children:
@@ -270,20 +271,20 @@
             return None
         newnode = cls()
         newnode.append(newc)
-        return newnode 
-        
+        return newnode
+
     def visit_and(self, et):
         return self._visit_binary(et, nodes.And)
 
     def visit_or(self, ou):
         return self._visit_binary(ou, nodes.Or)
-        
+
     def visit_not(self, node):
         return self._visit_unary(node, nodes.Not)
 
     def visit_exists(self, node):
         return self._visit_unary(node, nodes.Exists)
-   
+
     def visit_relation(self, relation):
         lhs, rhs = relation.get_variable_parts()
         if lhs.name == 'X':
@@ -301,7 +302,7 @@
             if relation.r_type in self.rhs_rels and self._may_be_shared(relation, 'subject'):
                 # ok, can share variable
                 term = self.rhs_rels[relation.r_type].children[0]
-                self._use_outer_term(lhs.name, term)            
+                self._use_outer_term(lhs.name, term)
                 return
         rel = nodes.Relation(relation.r_type, relation.optional)
         for c in relation.children:
@@ -319,7 +320,7 @@
         for c in cmp.children:
             cmp_.append(c.accept(self))
         return cmp_
-        
+
     def visit_function(self, function):
         """generate filter name for a function"""
         function_ = nodes.Function(function.name)
@@ -371,7 +372,7 @@
             for inserted_vref in insertedvar.references():
                 inserted_vref.parent.replace(inserted_vref, term.copy(self.select))
         self.rewritten[key] = term
-        
+
     def _get_varname_or_term(self, vname):
         if vname == 'U':
             if self.u_varname is None:
--- a/server/schemahooks.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/schemahooks.py	Wed Jun 03 09:15:20 2009 +0200
@@ -6,14 +6,15 @@
 checking for schema consistency is done in hooks.py
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
 from yams.schema import BASE_TYPES
 from yams.buildobjs import EntityType, RelationType, RelationDefinition
-from yams.schema2sql import eschema2sql, rschema2sql, _type_from_constraints
+from yams.schema2sql import eschema2sql, rschema2sql, type_from_constraints
 
 from cubicweb import ValidationError, RepositoryError
 from cubicweb.server import schemaserial as ss
@@ -21,7 +22,7 @@
 from cubicweb.server.pool import Operation, SingleLastOperation, PreCommitOperation
 from cubicweb.server.hookhelper import (entity_attr, entity_name,
                                      check_internal_entity)
-    
+
 # core entity and relation types which can't be removed
 CORE_ETYPES = list(BASE_TYPES) + ['CWEType', 'CWRType', 'CWUser', 'CWGroup',
                                   'CWConstraint', 'CWAttribute', 'CWRelation']
@@ -52,7 +53,7 @@
                                % (table, column)))
         session.info('added column %s to table %s', column, table)
     except:
-        # silent exception here, if this error has not been raised because the 
+        # silent exception here, if this error has not been raised because the
         # column already exists, index creation will fail anyway
         session.exception('error while adding column %s to table %s',
                           table, column)
@@ -74,7 +75,7 @@
         Operation.__init__(self, session, **kwargs)
         # every schema operation is triggering a schema update
         UpdateSchemaOp(session)
-        
+
 class EarlySchemaOperation(SchemaOperation):
     def insert_index(self):
         """schema operation which are inserted at the begining of the queue
@@ -85,7 +86,7 @@
             if not isinstance(op, EarlySchemaOperation):
                 return i
         return i + 1
-    
+
 class UpdateSchemaOp(SingleLastOperation):
     """the update schema operation:
 
@@ -93,15 +94,15 @@
     operations. It will trigger internal structures rebuilding to consider
     schema changes
     """
-    
+
     def __init__(self, session):
         self.repo = session.repo
         SingleLastOperation.__init__(self, session)
-        
+
     def commit_event(self):
         self.repo.set_schema(self.repo.schema)
 
-        
+
 class DropTableOp(PreCommitOperation):
     """actually remove a database from the application's schema"""
     table = None # make pylint happy
@@ -113,7 +114,7 @@
         dropped.add(self.table)
         self.session.system_sql('DROP TABLE %s' % self.table)
         self.info('dropped table %s', self.table)
-        
+
 class DropColumnOp(PreCommitOperation):
     """actually remove the attribut's column from entity table in the system
     database
@@ -130,12 +131,12 @@
         except Exception, ex:
             # not supported by sqlite for instance
             self.error('error while altering table %s: %s', table, ex)
-            
+
 
 # deletion ####################################################################
 
 class DeleteCWETypeOp(SchemaOperation):
-    """actually remove the entity type from the application's schema"""    
+    """actually remove the entity type from the application's schema"""
     def commit_event(self):
         try:
             # del_entity_type also removes entity's relations
@@ -162,9 +163,9 @@
     session.execute('DELETE State X WHERE NOT X state_of Y')
     session.execute('DELETE Transition X WHERE NOT X transition_of Y')
 
-        
+
 class DeleteCWRTypeOp(SchemaOperation):
-    """actually remove the relation type from the application's schema"""    
+    """actually remove the relation type from the application's schema"""
     def commit_event(self):
         try:
             self.schema.del_relation_type(self.kobj)
@@ -186,8 +187,8 @@
                     {'x': eid})
     DeleteCWRTypeOp(session, name)
 
-    
-class DelErdefOp(SchemaOperation):
+
+class DelRelationDefOp(SchemaOperation):
     """actually remove the relation definition from the application's schema"""
     def commit_event(self):
         subjtype, rtype, objtype = self.kobj
@@ -196,7 +197,7 @@
         except KeyError:
             # relation type may have been already deleted
             pass
-        
+
 def after_del_relation_type(session, rdefeid, rtype, rteid):
     """before deleting a CWAttribute or CWRelation entity:
     * if this is a final or inlined relation definition, instantiate an
@@ -223,7 +224,7 @@
     # we have to update physical schema systematically for final and inlined
     # relations, but only if it's the last instance for this relation type
     # for other relations
-    
+
     if (rschema.is_final() or rschema.inlined):
         rset = execute('Any COUNT(X) WHERE X is %s, X relation_type R, '
                        'R eid %%(x)s, X from_entity E, E name %%(name)s'
@@ -236,18 +237,18 @@
     # if this is the last instance, drop associated relation type
     if lastrel and not rteid in pendings:
         execute('DELETE CWRType X WHERE X eid %(x)s', {'x': rteid}, 'x')
-    DelErdefOp(session, (subjschema, rschema, objschema))
+    DelRelationDefOp(session, (subjschema, rschema, objschema))
 
-        
+
 # addition ####################################################################
 
 class AddCWETypeOp(EarlySchemaOperation):
-    """actually add the entity type to the application's schema"""    
+    """actually add the entity type to the application's schema"""
     eid = None # make pylint happy
     def commit_event(self):
         eschema = self.schema.add_entity_type(self.kobj)
         eschema.eid = self.eid
-        
+
 def before_add_eetype(session, entity):
     """before adding a CWEType entity:
     * check that we are not using an existing entity type,
@@ -304,25 +305,25 @@
 
 
 class AddCWRTypeOp(EarlySchemaOperation):
-    """actually add the relation type to the application's schema"""    
+    """actually add the relation type to the application's schema"""
     eid = None # make pylint happy
     def commit_event(self):
         rschema = self.schema.add_relation_type(self.kobj)
         rschema.set_default_groups()
         rschema.eid = self.eid
-        
+
 def before_add_ertype(session, entity):
     """before adding a CWRType entity:
     * check that we are not using an existing relation type,
     * register an operation to add the relation type to the application's
       schema on commit
-      
+
     We don't know yeat this point if a table is necessary
     """
     name = entity['name']
     if name in session.repo.schema.relations():
         raise RepositoryError('a relation type %s already exists' % name)
-    
+
 def after_add_ertype(session, entity):
     """after a CWRType entity has been added:
     * register an operation to add the relation type to the application's
@@ -340,7 +341,7 @@
 class AddErdefOp(EarlySchemaOperation):
     """actually add the attribute relation definition to the application's
     schema
-    """    
+    """
     def commit_event(self):
         self.schema.add_relation_def(self.kobj)
 
@@ -350,7 +351,7 @@
     'Float': float,
     'Password': str,
     'String': unicode,
-    'Date' : unicode, 
+    'Date' : unicode,
     'Datetime' : unicode,
     'Time' : unicode,
     }
@@ -362,7 +363,7 @@
     * set default on this column if any and possible
     * register an operation to add the relation definition to the
       application's schema on commit
-      
+
     constraints are handled by specific hooks
     """
     entity = None # make pylint happy
@@ -392,8 +393,8 @@
                                   constraints=constraints,
                                   eid=entity.eid)
         sysource = session.pool.source('system')
-        attrtype = _type_from_constraints(sysource.dbhelper, rdef.object,
-                                          constraints)
+        attrtype = type_from_constraints(sysource.dbhelper, rdef.object,
+                                         constraints)
         # XXX should be moved somehow into lgc.adbh: sqlite doesn't support to
         # add a new column with UNIQUE, it should be added after the ALTER TABLE
         # using ADD INDEX
@@ -422,20 +423,6 @@
             except Exception, ex:
                 self.error('error while creating index for %s.%s: %s',
                            table, column, ex)
-        # postgres doesn't implement, so do it in two times
-        # ALTER TABLE %s ADD COLUMN %s %s SET DEFAULT %s
-        if default is not None:
-            if isinstance(default, unicode):
-                default = default.encode(sysource.encoding)
-            try:
-                session.system_sql('ALTER TABLE %s ALTER COLUMN %s SET DEFAULT '
-                                   '%%(default)s' % (table, column),
-                                   {'default': default})
-            except Exception, ex:
-                # not supported by sqlite for instance
-                self.error('error while altering table %s: %s', table, ex)
-            session.system_sql('UPDATE %s SET %s=%%(default)s' % (table, column),
-                               {'default': default})
         AddErdefOp(session, rdef)
 
 def after_add_efrdef(session, entity):
@@ -457,7 +444,7 @@
         session = self.session
         entity = self.entity
         fromentity = entity.from_entity[0]
-        relationtype = entity.relation_type[0] 
+        relationtype = entity.relation_type[0]
         session.execute('SET X ordernum Y+1 WHERE X from_entity SE, SE eid %(se)s, X ordernum Y, X ordernum >= %(order)s, NOT X eid %(x)s',
                         {'x': entity.eid, 'se': fromentity.eid, 'order': entity.ordernum or 0})
         subj, rtype = str(fromentity.name), str(relationtype.name)
@@ -508,7 +495,7 @@
                     if sql.strip():
                         self.session.system_sql(sql)
                 session.add_query_data('createdtables', rtype)
-                
+
 def after_add_enfrdef(session, entity):
     AddCWRelationPreCommitOp(session, entity=entity)
 
@@ -556,32 +543,45 @@
                 (self.newname, self.oldname))
         sqlexec('UPDATE deleted_entities SET type=%s WHERE type=%s',
                 (self.newname, self.oldname))
-        
+
     def commit_event(self):
         self.session.repo.schema.rename_entity_type(self.oldname, self.newname)
 
 
-class UpdateRdefOp(SchemaOperation):
+class UpdateRelationDefOp(SchemaOperation):
     """actually update some properties of a relation definition"""
     rschema = values = None # make pylint happy
 
     def precommit_event(self):
+        etype = self.kobj[0]
+        table = SQL_PREFIX + etype
+        column = SQL_PREFIX + self.rschema.type
         if 'indexed' in self.values:
             sysource = self.session.pool.source('system')
-            etype, rtype = self.kobj[0], self.rschema.type
-            table = SQL_PREFIX + etype
-            column = SQL_PREFIX + rtype
             if self.values['indexed']:
                 sysource.create_index(self.session, table, column)
             else:
                 sysource.drop_index(self.session, table, column)
-                
+        if 'cardinality' in self.values and self.rschema.is_final():
+            adbh = self.session.pool.source('system').dbhelper
+            if not adbh.alter_column_support:
+                # not supported (and NOT NULL not set by yams in that case, so
+                # no worry)
+                return
+            atype = self.rschema.objects(etype)[0]
+            constraints = self.rschema.rproperty(etype, atype, 'constraints')
+            coltype = type_from_constraints(adbh, atype, constraints,
+                                            creating=False)
+            sql = adbh.sql_set_null_allowed(table, column, coltype,
+                                            self.values['cardinality'][0] != '1')
+            self.session.system_sql(sql)
+
     def commit_event(self):
         # structure should be clean, not need to remove entity's relations
         # at this point
         self.rschema._rproperties[self.kobj].update(self.values)
 
-    
+
 def after_update_erdef(session, entity):
     desttype = entity.to_entity[0].name
     rschema = session.repo.schema[entity.relation_type[0].name]
@@ -595,12 +595,12 @@
             newvalues[prop] = entity[prop]
     if newvalues:
         subjtype = entity.from_entity[0].name
-        UpdateRdefOp(session, (subjtype, desttype), rschema=rschema,
-                     values=newvalues)
+        UpdateRelationDefOp(session, (subjtype, desttype),
+                            rschema=rschema, values=newvalues)
 
 
 class UpdateRtypeOp(SchemaOperation):
-    """actually update some properties of a relation definition"""    
+    """actually update some properties of a relation definition"""
     rschema = values = entity = None # make pylint happy
 
     def precommit_event(self):
@@ -639,13 +639,13 @@
         else:
             for etype in rschema.subjects():
                 try:
-                    add_inline_relation_column(session, str(etype), rtype)                    
+                    add_inline_relation_column(session, str(etype), rtype)
                 except Exception, ex:
                     # the column probably already exists. this occurs when
                     # the entity's type has just been added or if the column
                     # has not been previously dropped
                     self.error('error while altering table %s: %s', etype, ex)
-                # copy existant data. 
+                # copy existant data.
                 # XXX don't use, it's not supported by sqlite (at least at when i tried it)
                 #sqlexec('UPDATE %(etype)s SET %(rtype)s=eid_to '
                 #        'FROM %(rtype)s_relation '
@@ -667,7 +667,7 @@
         # structure should be clean, not need to remove entity's relations
         # at this point
         self.rschema.__dict__.update(self.values)
-    
+
 def after_update_ertype(session, entity):
     rschema = session.repo.schema.rschema(entity.name)
     newvalues = {}
@@ -684,13 +684,13 @@
 class ConstraintOp(SchemaOperation):
     """actually update constraint of a relation definition"""
     entity = None # make pylint happy
-    
+
     def prepare_constraints(self, rtype, subjtype, objtype):
         constraints = rtype.rproperty(subjtype, objtype, 'constraints')
         self.constraints = list(constraints)
         rtype.set_rproperty(subjtype, objtype, 'constraints', self.constraints)
         return self.constraints
-    
+
     def precommit_event(self):
         rdef = self.entity.reverse_constrained_by[0]
         session = self.session
@@ -698,7 +698,7 @@
         # is created by AddEN?FRDefPreCommitOp, there is nothing to do here
         if rdef.eid in session.query_data('neweids', ()):
             self.cancelled = True
-            return 
+            return
         self.cancelled = False
         schema = session.repo.schema
         subjtype, rtype, objtype = schema.schema_by_eid(rdef.eid)
@@ -712,9 +712,13 @@
         # alter the physical schema on size constraint changes
         if self._cstr.type() == 'SizeConstraint' and (
             self.cstr is None or self.cstr.max != self._cstr.max):
+            adbh = self.session.pool.source('system').dbhelper
+            card = rtype.rproperty(subjtype, objtype, 'cardinality')
+            coltype = type_from_constraints(adbh, objtype, [self._cstr],
+                                            creating=False)
+            sql = adbh.sql_change_col_type(table, column, coltype, card != '1')
             try:
-                session.system_sql('ALTER TABLE %s ALTER COLUMN %s TYPE VARCHAR(%s)'
-                                   % (table, column, self._cstr.max))
+                session.system_sql(sql)
                 self.info('altered column %s of table %s: now VARCHAR(%s)',
                           column, table, self._cstr.max)
             except Exception, ex:
@@ -723,11 +727,11 @@
         elif cstrtype == 'UniqueConstraint':
             session.pool.source('system').create_index(
                 self.session, table, column, unique=True)
-        
+
     def commit_event(self):
         if self.cancelled:
             return
-        # in-place removing
+        # in-place modification
         if not self.cstr is None:
             self.constraints.remove(self.cstr)
         self.constraints.append(self._cstr)
@@ -743,7 +747,7 @@
 class DelConstraintOp(ConstraintOp):
     """actually remove a constraint of a relation definition"""
     rtype = subjtype = objtype = None # make pylint happy
-    
+
     def precommit_event(self):
         self.prepare_constraints(self.rtype, self.subjtype, self.objtype)
         cstrtype = self.cstr.type()
@@ -754,7 +758,7 @@
             try:
                 self.session.system_sql('ALTER TABLE %s ALTER COLUMN %s TYPE TEXT'
                                         % (table, column))
-                self.info('altered column %s of table %s: now TEXT', 
+                self.info('altered column %s of table %s: now TEXT',
                           column, table)
             except Exception, ex:
                 # not supported by sqlite for instance
@@ -762,7 +766,7 @@
         elif cstrtype == 'UniqueConstraint':
             self.session.pool.source('system').drop_index(
                 self.session, table, column, unique=True)
-                
+
     def commit_event(self):
         self.constraints.remove(self.cstr)
 
@@ -784,7 +788,7 @@
     if fromeid in session.query_data('neweids', ()):
         session.add_query_data(fromeid, toeid)
 
-    
+
 # schema permissions synchronization ##########################################
 
 class PermissionOp(Operation):
@@ -805,7 +809,7 @@
     def __init__(self, session, perm, etype_eid, group_eid):
         self.group = entity_name(session, group_eid)
         PermissionOp.__init__(self, session, perm, etype_eid)
-        
+
     def commit_event(self):
         """the observed connections pool has been commited"""
         try:
@@ -815,7 +819,7 @@
             self.error('no schema for %s', self.name)
             return
         groups = list(erschema.get_groups(self.perm))
-        try:            
+        try:
             groups.index(self.group)
             self.warning('group %s already have permission %s on %s',
                          self.group, self.perm, erschema.type)
@@ -830,7 +834,7 @@
     def __init__(self, session, perm, etype_eid, expression):
         self.expr = expression
         PermissionOp.__init__(self, session, perm, etype_eid)
-        
+
     def commit_event(self):
         """the observed connections pool has been commited"""
         try:
@@ -852,12 +856,12 @@
         expr = session.execute('Any EXPR WHERE X eid %(x)s, X expression EXPR',
                                {'x': object}, 'x')[0][0]
         AddRQLExpressionPermissionOp(session, perm, subject, expr)
-    
+
 
-        
+
 class DelGroupPermissionOp(AddGroupPermissionOp):
     """synchronize schema when a *_permission relation has been deleted from a group"""
-        
+
     def commit_event(self):
         """the observed connections pool has been commited"""
         try:
@@ -867,17 +871,17 @@
             self.error('no schema for %s', self.name)
             return
         groups = list(erschema.get_groups(self.perm))
-        try:            
+        try:
             groups.remove(self.group)
             erschema.set_groups(self.perm, groups)
         except ValueError:
             self.error('can\'t remove permission %s on %s to group %s',
                 self.perm, erschema.type, self.group)
 
-        
+
 class DelRQLExpressionPermissionOp(AddRQLExpressionPermissionOp):
     """synchronize schema when a *_permission relation has been deleted from an rql expression"""
-        
+
     def commit_event(self):
         """the observed connections pool has been commited"""
         try:
@@ -897,7 +901,7 @@
             return
         erschema.set_rqlexprs(self.perm, rqlexprs)
 
-                
+
 def before_del_permission(session, subject, rtype, object):
     """delete entity/relation *_permission, need to update schema
 
@@ -943,7 +947,7 @@
     hm.register_hook(before_del_ertype, 'before_delete_entity', 'CWRType')
     hm.register_hook(after_del_relation_type, 'after_delete_relation', 'relation_type')
     hm.register_hook(rebuild_infered_relations, 'after_add_relation', 'specializes')
-    hm.register_hook(rebuild_infered_relations, 'after_delete_relation', 'specializes')    
+    hm.register_hook(rebuild_infered_relations, 'after_delete_relation', 'specializes')
     # constraints synchronization hooks
     hm.register_hook(after_add_econstraint, 'after_add_entity', 'CWConstraint')
     hm.register_hook(after_update_econstraint, 'after_update_entity', 'CWConstraint')
--- a/server/schemaserial.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/schemaserial.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """functions for schema / permissions (de)serialization using RQL
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -14,6 +15,7 @@
 from yams import schema as schemamod, buildobjs as ybo
 
 from cubicweb.schema import CONSTRAINTS, ETYPE_NAME_MAP
+from cubicweb.server import sqlutils
 
 def group_mapping(cursor, interactive=True):
     """create a group mapping from an rql cursor
@@ -50,7 +52,7 @@
 def _set_sql_prefix(prefix):
     """3.2.0 migration function: allow to unset/reset SQL_PREFIX"""
     for module in ('checkintegrity', 'migractions', 'schemahooks',
-                   'sources.rql2sql', 'sources.native'):
+                   'sources.rql2sql', 'sources.native', 'sqlutils'):
         try:
             sys.modules['cubicweb.server.%s' % module].SQL_PREFIX = prefix
             print 'changed SQL_PREFIX for %s' % module
@@ -124,7 +126,8 @@
         if etype in ETYPE_NAME_MAP: # XXX <2.45 bw compat
             print 'fixing etype name from %s to %s' % (etype, ETYPE_NAME_MAP[etype])
             # can't use write rql queries at this point, use raw sql
-            session.system_sql('UPDATE CWEType SET name=%(n)s WHERE eid=%(x)s',
+            session.system_sql('UPDATE %(p)sCWEType SET %(p)sname=%%(n)s WHERE %(p)seid=%%(x)s'
+                               % {'p': sqlutils.SQL_PREFIX},
                                {'x': eid, 'n': ETYPE_NAME_MAP[etype]})
             session.system_sql('UPDATE entities SET type=%(n)s WHERE type=%(x)s',
                                {'x': etype, 'n': ETYPE_NAME_MAP[etype]})
--- a/server/securityhooks.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/securityhooks.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 the user connected to a session
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -24,25 +25,25 @@
         if rschema.is_final(): # non final relation are checked by other hooks
             # add/delete should be equivalent (XXX: unify them into 'update' ?)
             rschema.check_perm(session, 'add', eid)
-            
-    
+
+
 class CheckEntityPermissionOp(LateOperation):
     def precommit_event(self):
         #print 'CheckEntityPermissionOp', self.session.user, self.entity, self.action
         self.entity.check_perm(self.action)
         check_entity_attributes(self.session, self.entity)
-        
+
     def commit_event(self):
         pass
-            
-    
+
+
 class CheckRelationPermissionOp(LateOperation):
     def precommit_event(self):
         self.rschema.check_perm(self.session, self.action, self.fromeid, self.toeid)
-        
+
     def commit_event(self):
         pass
-    
+
 def after_add_entity(session, entity):
     if not session.is_super_session:
         CheckEntityPermissionOp(session, entity=entity, action='add')
@@ -56,7 +57,7 @@
         except Unauthorized:
             entity.clear_local_perm_cache('update')
             CheckEntityPermissionOp(session, entity=entity, action='update')
-        
+
 def before_del_entity(session, eid):
     if not session.is_super_session:
         eschema = session.repo.schema[session.describe(eid)[0]]
@@ -67,7 +68,7 @@
     if rtype in BEFORE_ADD_RELATIONS and not session.is_super_session:
         rschema = session.repo.schema[rtype]
         rschema.check_perm(session, 'add', fromeid, toeid)
-        
+
 def after_add_relation(session, fromeid, rtype, toeid):
     if not rtype in BEFORE_ADD_RELATIONS and not session.is_super_session:
         rschema = session.repo.schema[rtype]
@@ -89,4 +90,4 @@
     hm.register_hook(before_add_relation, 'before_add_relation', '')
     hm.register_hook(after_add_relation, 'after_add_relation', '')
     hm.register_hook(before_del_relation, 'before_delete_relation', '')
-    
+
--- a/server/server.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/server.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Pyro RQL server
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -22,7 +23,7 @@
     """base event"""
     # timefunc = staticmethod(localtime)
     timefunc = localtime
-    
+
     def __init__(self, absolute=None, period=None):
         # local time tuple
         if absolute is None:
@@ -57,10 +58,10 @@
     def fire(self, server):
         server.repo.shutdown()
         server.quiting = True
-        
+
 
 class RepositoryServer(object):
-    
+
     def __init__(self, config, debug=False):
         """make the repository available as a PyRO object"""
         self.config = config
@@ -86,7 +87,7 @@
                     event.update()
                 except Finished:
                     self.events.remove(event)
-            
+
     def run(self, req_timeout=5.0):
         """enter the service loop"""
         while self.quiting is None:
@@ -95,7 +96,7 @@
             except select.error:
                 continue
             self.trigger_events()
-    
+
     def quit(self):
         """stop the server"""
         self.add_event(QuitEvent())
@@ -105,16 +106,16 @@
         necessary
         """
         self.daemon = self.repo.pyro_register(host)
-            
+
     # server utilitities ######################################################
-    
+
     def install_sig_handlers(self):
         """install signal handlers"""
         import signal
         self.info('installing signal handlers')
         signal.signal(signal.SIGINT, lambda x, y, s=self: s.quit())
         signal.signal(signal.SIGTERM, lambda x, y, s=self: s.quit())
-        
+
     def daemonize(self, pid_file=None):
         """daemonize the process"""
         # fork so the parent can exist
--- a/server/serverconfig.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/serverconfig.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """server.serverconfig definition
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/server/serverctl.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/serverctl.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,11 +1,13 @@
 """cubicweb-ctl commands and command handlers specific to the server.serverconfig
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
+import sys
 import os
 
 from logilab.common.configuration import REQUIRED, Configuration, ini_format_section
@@ -117,7 +119,7 @@
                     _sconfig.set_option(attr, val)
             sconfig = _sconfig
         optsbysect = list(sconfig.options_by_section())
-        assert len(optsbysect) == 1
+        assert len(optsbysect) == 1, 'all options for a source should be in the same group'
         ini_format_section(stream, uri, optsbysect[0][1])
         if hasattr(sconfig, 'adapter'):
             print >> stream
@@ -418,6 +420,51 @@
             cnx.commit()
             print 'grants given to %s on application %s' % (appid, user)
 
+class ResetAdminPasswordCommand(Command):
+    """Reset the administrator password.
+
+    <application>
+      the identifier of the application
+    """
+    name = 'reset-admin-pwd'
+    arguments = '<application>'
+
+    def run(self, args):
+        """run the command with its specific arguments"""
+        from cubicweb.server.sqlutils import sqlexec, SQL_PREFIX
+        from cubicweb.server.utils import crypt_password, manager_userpasswd
+        appid = pop_arg(args, 1, msg="No application specified !")
+        config = ServerConfiguration.config_for(appid)
+        sourcescfg = config.sources()
+        try:
+            adminlogin = sourcescfg['admin']['login']
+        except KeyError:
+            print 'could not get cubicweb administrator login'
+            sys.exit(1)
+        cnx = source_cnx(sourcescfg['system'])
+        cursor = cnx.cursor()
+        _, passwd = manager_userpasswd(adminlogin, confirm=True,
+                                       passwdmsg='new password for %s' % adminlogin)
+        try:
+            sqlexec("UPDATE %(sp)sCWUser SET %(sp)supassword='%(p)s' WHERE %(sp)slogin='%(l)s'"
+                    % {'sp': SQL_PREFIX,
+                       'p': crypt_password(passwd), 'l': adminlogin},
+                    cursor, withpb=False)
+            sconfig = Configuration(options=USER_OPTIONS)
+            sconfig['login'] = adminlogin
+            sconfig['password'] = passwd
+            sourcescfg['admin'] = sconfig
+            sourcesfile = config.sources_file()
+            generate_sources_file(sourcesfile, sourcescfg)
+            restrict_perms_to_user(sourcesfile)
+        except Exception, ex:
+            cnx.rollback()
+            import traceback
+            traceback.print_exc()
+            print 'An error occured:', ex
+        else:
+            cnx.commit()
+            print 'password reset, sources file regenerated'
 
 
 class StartRepositoryCommand(Command):
@@ -735,6 +782,7 @@
 register_commands( (CreateApplicationDBCommand,
                     InitApplicationCommand,
                     GrantUserOnApplicationCommand,
+                    ResetAdminPasswordCommand,
                     StartRepositoryCommand,
                     DBDumpCommand,
                     DBRestoreCommand,
--- a/server/session.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/session.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Repository users' and internal' sessions.
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -65,6 +66,8 @@
         # i18n initialization
         self.set_language(cnxprops.lang)
         self._threaddata = threading.local()
+        self._threads_in_transaction = set()
+        self._closed = False
 
     def get_mode(self):
         return getattr(self._threaddata, 'mode', 'read')
@@ -150,6 +153,8 @@
 
     def set_pool(self):
         """the session need a pool to execute some queries"""
+        if self._closed:
+            raise Exception('try to set pool on a closed session')
         if self.pool is None:
             self._threaddata.pool = self.repo._get_pool()
             try:
@@ -158,6 +163,7 @@
                 self.repo._free_pool(self.pool)
                 self._threaddata.pool = None
                 raise
+            self._threads_in_transaction.add(threading.currentThread())
         return self._threaddata.pool
 
     def reset_pool(self):
@@ -167,6 +173,7 @@
         # or rollback
         if self.pool is not None and self.mode == 'read':
             # even in read mode, we must release the current transaction
+            self._threads_in_transaction.remove(threading.currentThread())
             self.repo._free_pool(self.pool)
             self.pool.pool_reset(self)
             self._threaddata.pool = None
@@ -343,6 +350,23 @@
 
     def close(self):
         """do not close pool on session close, since they are shared now"""
+        self._closed = True
+        # copy since _threads_in_transaction maybe modified while waiting
+        for thread in self._threads_in_transaction.copy():
+            if thread is threading.currentThread():
+                continue
+            self.info('waiting for thread %s', thread)
+            # do this loop/break instead of a simple join(10) in case thread is
+            # the main thread (in which case it will be removed from
+            # self._threads_in_transaction but still be alive...)
+            for i in xrange(10):
+                thread.join(1)
+                if not (thread.isAlive() and
+                        thread in self._threads_in_transaction):
+                    break
+            else:
+                self.error('thread %s still alive after 10 seconds, will close '
+                           'session anyway', thread)
         self.rollback()
 
     # transaction data/operations management ##################################
--- a/server/sources/__init__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/sources/__init__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """cubicweb server sources support
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/server/sources/extlite.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/sources/extlite.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """provide an abstract class for external sources using a sqlite database helper
 
 :organization: Logilab
-:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/server/sources/ldapuser.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/sources/ldapuser.py	Wed Jun 03 09:15:20 2009 +0200
@@ -3,7 +3,7 @@
 this source is for now limited to a read-only CWUser source
 
 :organization: Logilab
-:copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 
 
@@ -18,8 +18,11 @@
 WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 FOR A PARTICULAR PURPOSE.
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
+from base64 import b64decode
+
 from logilab.common.textutils import get_csv
 from rql.nodes import Relation, VariableRef, Constant, Function
 
@@ -52,14 +55,14 @@
 
 class LDAPUserSource(AbstractSource):
     """LDAP read-only CWUser source"""
-    support_entities = {'CWUser': False} 
+    support_entities = {'CWUser': False}
 
     port = None
-    
+
     cnx_mode = 0
     cnx_dn = ''
     cnx_pwd = ''
-    
+
     options = (
         ('host',
          {'type' : 'string',
@@ -119,9 +122,9 @@
           'help': 'life time of query cache in minutes (default to two hours).',
           'group': 'ldap-source', 'inputlevel': 2,
           }),
-        
+
     )
-            
+
     def __init__(self, repo, appschema, source_config, *args, **kwargs):
         AbstractSource.__init__(self, repo, appschema, source_config,
                                 *args, **kwargs)
@@ -150,8 +153,9 @@
 
     def init(self):
         """method called by the repository once ready to handle request"""
-        self.repo.looping_task(self._interval, self.synchronize) 
-        self.repo.looping_task(self._query_cache.ttl.seconds/10, self._query_cache.clear_expired) 
+        self.repo.looping_task(self._interval, self.synchronize)
+        self.repo.looping_task(self._query_cache.ttl.seconds/10,
+                               self._query_cache.clear_expired)
 
     def synchronize(self):
         """synchronize content known by this repository with content in the
@@ -166,10 +170,11 @@
         try:
             cursor = session.system_sql("SELECT eid, extid FROM entities WHERE "
                                         "source='%s'" % self.uri)
-            for eid, extid in cursor.fetchall():
+            for eid, b64extid in cursor.fetchall():
+                extid = b64decode(b64extid)
                 # if no result found, _search automatically delete entity information
                 res = self._search(session, extid, BASE)
-                if res: 
+                if res:
                     ldapemailaddr = res[0].get(ldap_emailattr)
                     if ldapemailaddr:
                         rset = session.execute('EmailAddress X,A WHERE '
@@ -192,13 +197,13 @@
         finally:
             session.commit()
             session.close()
-            
+
     def get_connection(self):
         """open and return a connection to the source"""
         if self._conn is None:
             self._connect()
         return ConnectionWrapper(self._conn)
-    
+
     def authenticate(self, session, login, password):
         """return CWUser eid for the given login/password if this account is
         defined in this source, else raise `AuthenticationError`
@@ -231,7 +236,7 @@
             relname = iter(var.stinfo['relations']).next().r_type
             return self.user_rev_attrs.get(relname)
         return None
-        
+
     def prepare_columns(self, mainvars, rqlst):
         """return two list describin how to build the final results
         from the result of an ldap search (ie a list of dictionnary)
@@ -270,11 +275,11 @@
             #    # probably a bug in rql splitting if we arrive here
             #    raise NotImplementedError
         return columns, global_transforms
-    
+
     def syntax_tree_search(self, session, union,
                            args=None, cachekey=None, varmap=None, debug=0):
-        """return result from this source for a rql query (actually from a rql 
-        syntax tree and a solution dictionary mapping each used variable to a 
+        """return result from this source for a rql query (actually from a rql
+        syntax tree and a solution dictionary mapping each used variable to a
         possible type). If cachekey is given, the query necessary to fetch the
         results (but not the results themselves) may be cached using this key.
         """
@@ -361,8 +366,8 @@
             result = trfunc.apply(result)
         #print '--> ldap result', result
         return result
-                
-    
+
+
     def _connect(self, userdn=None, userpwd=None):
         port, protocol = MODES[self.cnx_mode]
         if protocol == 'ldapi':
@@ -444,12 +449,12 @@
             result.append(rec_dict)
         #print '--->', result
         return result
-    
+
     def before_entity_insertion(self, session, lid, etype, eid):
         """called by the repository when an eid has been attributed for an
         entity stored here but the entity has not been inserted in the system
         table yet.
-        
+
         This method must return the an Entity instance representation of this
         entity.
         """
@@ -458,7 +463,7 @@
         for attr in entity.e_schema.indexable_attributes():
             entity[attr] = res[self.user_rev_attrs[attr]]
         return entity
-    
+
     def after_entity_insertion(self, session, dn, entity):
         """called by the repository after an entity stored here has been
         inserted in the system table.
@@ -492,13 +497,13 @@
 def _insert_email(session, emailaddr, ueid):
     session.execute('INSERT EmailAddress X: X address %(addr)s, U primary_email X '
                     'WHERE U eid %(x)s', {'addr': emailaddr, 'x': ueid}, 'x')
-    
+
 class GotDN(Exception):
     """exception used when a dn localizing the searched user has been found"""
     def __init__(self, dn):
         self.dn = dn
 
-        
+
 class RQL2LDAPFilter(object):
     """generate an LDAP filter for a rql query"""
     def __init__(self, source, session, args=None, mainvars=()):
@@ -510,7 +515,7 @@
             args = {}
         self._args = args
         self.mainvars = mainvars
-        
+
     def generate(self, selection, mainvarname):
         self._filters = res = self._base_filters[:]
         self._mainvarname = mainvarname
@@ -527,7 +532,7 @@
         if len(res) > 1:
             return self._eidfilters, '(&%s)' % ''.join(res)
         return self._eidfilters, res[0]
-    
+
     def visit_and(self, et):
         """generate filter for a AND subtree"""
         for c in et.children:
@@ -587,7 +592,7 @@
         else:
             raise NotImplementedError(relation)
         return res
-        
+
     def _visit_attribute_relation(self, relation):
         """generate filter for an attribute relation"""
         lhs, rhs = relation.get_parts()
@@ -623,18 +628,18 @@
 
     def visit_comparison(self, cmp):
         """generate filter for a comparaison"""
-        return '%s%s'% (cmp.operator, cmp.children[0].accept(self))            
+        return '%s%s'% (cmp.operator, cmp.children[0].accept(self))
 
     def visit_mathexpression(self, mexpr):
         """generate filter for a mathematic expression"""
         raise NotImplementedError
-        
+
     def visit_function(self, function):
         """generate filter name for a function"""
         if function.name == 'IN':
             return self.visit_in(function)
         raise NotImplementedError
-        
+
     def visit_in(self, function):
         grandpapa = function.parent.parent
         ldapattr = self._ldap_attrs[grandpapa.r_type]
@@ -649,7 +654,7 @@
             else:
                 part = '(%s=%s)' % (ldapattr, res[0])
         return part
-        
+
     def visit_constant(self, constant):
         """generate filter name for a constant"""
         value = constant.value
@@ -667,7 +672,7 @@
         else:
             value = str(value)
         return escape_filter_chars(value)
-        
+
     def visit_variableref(self, variableref):
         """get the sql name for a variable reference"""
         pass
--- a/server/sources/native.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/sources/native.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,13 +1,21 @@
 """Adapters for native cubicweb sources.
 
+Notes:
+* extid (aka external id, the primary key of an entity in the external source
+  from which it comes from) are stored in a varchar column encoded as a base64
+  string. This is because it should actually be Bytes but we want an index on
+  it for fast querying.
+  
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
 from threading import Lock
 from datetime import datetime
+from base64 import b64decode, b64encode
 
 from logilab.common.cache import Cache
 from logilab.common.configuration import REQUIRED
@@ -234,7 +242,7 @@
                 pwd = rset[0][0]
             except IndexError:
                 raise AuthenticationError('bad login')
-            # passwords are stored using the bytea type, so we get a StringIO
+            # passwords are stored using the Bytes type, so we get a StringIO
             if pwd is not None:
                 args['pwd'] = crypt_password(password, pwd.getvalue()[:2])
         # get eid from login and (crypted) password
@@ -456,22 +464,25 @@
             raise UnknownEid(eid)
         if res is None:
             raise UnknownEid(eid)
+        if res[-1] is not None:
+            if not isinstance(res, list):
+                res = list(res)
+            res[-1] = b64decode(res[-1])
         return res
 
-    def extid2eid(self, session, source, lid):
-        """get eid from a local id. An eid is attributed if no record is found"""
+    def extid2eid(self, session, source, extid):
+        """get eid from an external id. Return None if no record found."""
+        assert isinstance(extid, str)
         cursor = session.system_sql('SELECT eid FROM entities WHERE '
                                     'extid=%(x)s AND source=%(s)s',
-                                    # str() necessary with pg 8.3
-                                    {'x': str(lid), 's': source.uri})
+                                    {'x': b64encode(extid), 's': source.uri})
         # XXX testing rowcount cause strange bug with sqlite, results are there
         #     but rowcount is 0
         #if cursor.rowcount > 0:
         try:
             result = cursor.fetchone()
             if result:
-                eid = result[0]
-                return eid
+                return result[0]
         except:
             pass
         return None
@@ -499,8 +510,11 @@
     def add_info(self, session, entity, source, extid=None):
         """add type and source info for an eid into the system table"""
         # begin by inserting eid/type/source/extid into the entities table
-        attrs = {'type': str(entity.e_schema), 'eid': entity.eid,
-                 'extid': extid, 'source': source.uri, 'mtime': datetime.now()}
+        if extid is not None:
+            assert isinstance(extid, str)
+            extid = b64encode(extid)
+        attrs = {'type': entity.id, 'eid': entity.eid, 'extid': extid,
+                 'source': source.uri, 'mtime': datetime.now()}
         session.system_sql(self.sqlgen.insert('entities', attrs), attrs)
 
     def delete_info(self, session, eid, etype, uri, extid):
@@ -510,6 +524,9 @@
         attrs = {'eid': eid}
         session.system_sql(self.sqlgen.delete('entities', attrs), attrs)
         if self.has_deleted_entitites_table:
+            if extid is not None:
+                assert isinstance(extid, str), type(extid)
+                extid = b64encode(extid)
             attrs = {'type': etype, 'eid': eid, 'extid': extid,
                      'source': uri, 'dtime': datetime.now()}
             session.system_sql(self.sqlgen.insert('deleted_entities', attrs), attrs)
--- a/server/sources/pyrorql.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/sources/pyrorql.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,16 +1,17 @@
 """Source to query another RQL repository using pyro
 
 :organization: Logilab
-:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
 import threading
 from os.path import join
-
 from time import mktime
 from datetime import datetime
+from base64 import b64decode
 
 from Pyro.errors import PyroError, ConnectionClosedError
 
@@ -177,7 +178,7 @@
                 try:
                     exturi = cnx.describe(extid)[1]
                     if exturi == 'system' or not exturi in repo.sources_by_uri:
-                        eid = self.extid2eid(extid, etype, session)
+                        eid = self.extid2eid(str(extid), etype, session)
                         rset = session.eid_rset(eid, etype)
                         entity = rset.get_entity(0, 0)
                         entity.complete(entity.e_schema.indexable_attributes())
@@ -188,7 +189,8 @@
                     continue
             for etype, extid in deleted:
                 try:
-                    eid = self.extid2eid(extid, etype, session, insert=False)
+                    eid = self.extid2eid(str(extid), etype, session,
+                                         insert=False)
                     # entity has been deleted from external repository but is not known here
                     if eid is not None:
                         repo.delete_info(session, eid)
@@ -307,7 +309,8 @@
                             etype = descr[rowindex][colindex]
                             exttype, exturi, extid = cnx.describe(row[colindex])
                             if exturi == 'system' or not exturi in self.repo.sources_by_uri:
-                                eid = self.extid2eid(row[colindex], etype, session)
+                                eid = self.extid2eid(str(row[colindex]), etype,
+                                                     session)
                                 row[colindex] = eid
                             else:
                                 # skip this row
@@ -342,12 +345,14 @@
         cu = session.pool[self.uri]
         cu.execute('SET %s WHERE X eid %%(x)s' % ','.join(relations),
                    kwargs, 'x')
+        self._query_cache.clear()
 
     def delete_entity(self, session, etype, eid):
         """delete an entity from the source"""
         cu = session.pool[self.uri]
         cu.execute('DELETE %s X WHERE X eid %%(x)s' % etype,
                    {'x': self.eid2extid(eid, session)}, 'x')
+        self._query_cache.clear()
 
     def add_relation(self, session, subject, rtype, object):
         """add a relation to the source"""
@@ -355,6 +360,7 @@
         cu.execute('SET X %s Y WHERE X eid %%(x)s, Y eid %%(y)s' % rtype,
                    {'x': self.eid2extid(subject, session),
                     'y': self.eid2extid(object, session)}, ('x', 'y'))
+        self._query_cache.clear()
 
     def delete_relation(self, session, subject, rtype, object):
         """delete a relation from the source"""
@@ -362,6 +368,7 @@
         cu.execute('DELETE X %s Y WHERE X eid %%(x)s, Y eid %%(y)s' % rtype,
                    {'x': self.eid2extid(subject, session),
                     'y': self.eid2extid(object, session)}, ('x', 'y'))
+        self._query_cache.clear()
 
 
 class RQL2RQL(object):
@@ -490,7 +497,7 @@
             # XXX what about optional relation or outer NOT EXISTS()
             raise
         except ReplaceByInOperator, ex:
-            rhs = 'IN (%s)' % ','.join(str(eid) for eid in ex.eids)
+            rhs = 'IN (%s)' % ','.join(eid for eid in ex.eids)
         self.need_translation = False
         self.current_operator = None
         if node.optional in ('right', 'both'):
@@ -563,17 +570,15 @@
         except UnknownEid:
             operator = self.current_operator
             if operator is not None and operator != '=':
-                # deal with query like X eid > 12
+                # deal with query like "X eid > 12"
                 #
-                # The problem is
-                # that eid order in the external source may differ from the
-                # local source
+                # The problem is that eid order in the external source may
+                # differ from the local source
                 #
-                # So search for all eids from this
-                # source matching the condition locally and then to replace the
-                # > 12 branch by IN (eids) (XXX we may have to insert a huge
-                # number of eids...)
-                # planner so that
+                # So search for all eids from this source matching the condition
+                # locally and then to replace the "> 12" branch by "IN (eids)"
+                #
+                # XXX we may have to insert a huge number of eids...)
                 sql = "SELECT extid FROM entities WHERE source='%s' AND type IN (%s) AND eid%s%s"
                 etypes = ','.join("'%s'" % etype for etype in self.current_etypes)
                 cu = self._session.system_sql(sql % (self.source.uri, etypes,
@@ -582,6 +587,6 @@
                 # results
                 rows = cu.fetchall()
                 if rows:
-                    raise ReplaceByInOperator((r[0] for r in rows))
+                    raise ReplaceByInOperator((b64decode(r[0]) for r in rows))
             raise
 
--- a/server/sources/rql2sql.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/sources/rql2sql.py	Wed Jun 03 09:15:20 2009 +0200
@@ -17,7 +17,7 @@
 
    -> direct join between nonfinal1 and nonfinal2, whatever X,Y, Z (unless
       inlined...)
-      
+
       NOT IMPLEMENTED (and quite hard to implement)
 
 Potential optimization information is collected by the querier, sql generation
@@ -25,8 +25,9 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -41,7 +42,7 @@
 from cubicweb.server.sqlutils import SQL_PREFIX
 from cubicweb.server.utils import cleanup_solutions
 
-def _new_var(select, varname): 
+def _new_var(select, varname):
     newvar = select.get_variable(varname)
     if not 'relations' in newvar.stinfo:
         # not yet initialized
@@ -61,7 +62,7 @@
                 _fill_to_wrap_rel(vref.variable, newselect, towrap, schema)
         elif rschema.is_final():
             towrap.add( (var, rel) )
-   
+
 def rewrite_unstable_outer_join(select, solutions, unstable, schema):
     """if some optional variables are unstable, they should be selected in a
     subquery. This function check this and rewrite the rql syntax tree if
@@ -104,7 +105,7 @@
                 var.stinfo['relations'].add(newrel)
                 var.stinfo['rhsrelations'].add(newrel)
                 if rel.optional in ('right', 'both'):
-                    var.stinfo['optrelations'].add(newrel)                
+                    var.stinfo['optrelations'].add(newrel)
         # extract subquery solutions
         solutions = [sol.copy() for sol in solutions]
         cleanup_solutions(newselect, solutions)
@@ -205,7 +206,7 @@
                 for vref in term.iget_nodes(VariableRef):
                     if not vref in groups:
                         groups.append(vref)
-        
+
 def fix_selection(rqlst, selectedidx, needwrap, sorts, groups, having):
     if sorts:
         sort_term_selection(sorts, selectedidx, rqlst, not needwrap and groups)
@@ -230,7 +231,7 @@
         self.existssols = existssols
         self.unstablevars = unstablevars
         self.subtables = {}
-        
+
     def reset(self, solution):
         """reset some visit variables"""
         self.solution = solution
@@ -246,11 +247,11 @@
         self.aliases = {}
         self.restrictions = []
         self._restr_stack = []
-        
+
     def add_restriction(self, restr):
         if restr:
             self.restrictions.append(restr)
-            
+
     def iter_exists_sols(self, exists):
         if not exists in self.existssols:
             yield 1
@@ -286,8 +287,8 @@
         restrictions = self.restrictions
         self.restrictions = self._restr_stack.pop()
         return restrictions, self.actual_tables.pop()
-    
-    
+
+
 class SQLGenerator(object):
     """
     generation of SQL from the fully expanded RQL syntax tree
@@ -295,13 +296,13 @@
 
     Groups and sort are not handled here since they should not be handled at
     this level (see cubicweb.server.querier)
-    
+
     we should not have errors here !
 
     WARNING: a CubicWebSQLGenerator instance is not thread safe, but generate is
     protected by a lock
     """
-    
+
     def __init__(self, schema, dbms_helper, dbencoding='UTF-8'):
         self.schema = schema
         self.dbms_helper = dbms_helper
@@ -312,7 +313,7 @@
         if not self.dbms_helper.union_parentheses_support:
             self.union_sql = self.noparen_union_sql
         self._lock = threading.Lock()
-        
+
     def generate(self, union, args=None, varmap=None):
         """return SQL queries and a variable dictionnary from a RQL syntax tree
 
@@ -355,7 +356,7 @@
         sqls = (self.select_sql(select, needalias)
                 for i, select in enumerate(union.children))
         return '\nUNION ALL\n'.join(sqls)
-    
+
     def select_sql(self, select, needalias=False):
         """return SQL queries and a variable dictionnary from a RQL syntax tree
 
@@ -388,7 +389,7 @@
                 # query will be necessary
                 if groups or select.has_aggregat:
                     select.select_only_variables()
-                    needwrap = True                        
+                    needwrap = True
         else:
             existssols, unstable = {}, ()
         state = StateInfo(existssols, unstable)
@@ -441,7 +442,7 @@
                 sql += '\nHAVING %s' % having
             # sort
             if sorts:
-                sql += '\nORDER BY %s' % ','.join(self._sortterm_sql(sortterm, 
+                sql += '\nORDER BY %s' % ','.join(self._sortterm_sql(sortterm,
                                                                      fselectidx)
                                                   for sortterm in sorts)
                 if fneedwrap:
@@ -497,7 +498,7 @@
             return '\nUNION\n'.join(sqls)
         else:
             return '\nUNION ALL\n'.join(sqls)
-        
+
     def _selection_sql(self, selected, distinct, needaliasing=False):
         clause = []
         for term in selected:
@@ -546,7 +547,7 @@
                 return '(%s)' % ' OR '.join(res)
             return res[0]
         return ''
-    
+
     def visit_not(self, node):
         self._state.push_scope()
         csql = node.children[0].accept(self)
@@ -581,7 +582,7 @@
         if not sqls:
             return ''
         return 'EXISTS(%s)' % ' UNION '.join(sqls)
-            
+
     def _visit_exists(self, exists):
         self._state.push_scope()
         restriction = exists.children[0].accept(self)
@@ -593,12 +594,12 @@
             return ''
         if not tables:
             # XXX could leave surrounding EXISTS() in this case no?
-            sql = 'SELECT 1 WHERE %s' % restriction 
+            sql = 'SELECT 1 WHERE %s' % restriction
         else:
             sql = 'SELECT 1 FROM %s WHERE %s' % (', '.join(tables), restriction)
         return sql
 
-    
+
     def visit_relation(self, relation):
         """generate SQL for a relation"""
         rtype = relation.r_type
@@ -691,7 +692,7 @@
             extrajoin = self._extra_join_sql(relation, '%s.%s' % (rid, relfield), termvar)
             if extrajoin:
                 yield extrajoin
-        
+
     def _visit_relation(self, relation, rschema):
         """generate SQL for a relation
 
@@ -718,10 +719,10 @@
         """
         left outer join syntax (optional=='right'):
           X relation Y?
-          
+
         right outer join syntax (optional=='left'):
           X? relation Y
-          
+
         full outer join syntaxes (optional=='both'):
           X? relation Y?
 
@@ -834,7 +835,7 @@
         lhssql = self._inlined_var_sql(relation.children[0].variable,
                                        relation.r_type)
         return '%s%s' % (lhssql, relation.children[1].accept(self, contextrels))
-    
+
     def _visit_attribute_relation(self, relation):
         """generate SQL for an attribute relation"""
         lhs, rhs = relation.get_parts()
@@ -897,10 +898,11 @@
             not_ = False
         return self.dbms_helper.fti_restriction_sql(alias, const.eval(self._args),
                                                     jointo, not_) + restriction
-        
+
     def visit_comparison(self, cmp, contextrels=None):
         """generate SQL for a comparaison"""
         if len(cmp.children) == 2:
+            # XXX occurs ?
             lhs, rhs = cmp.children
         else:
             lhs = None
@@ -911,6 +913,11 @@
                 operator = ' LIKE '
             else:
                 operator = ' %s ' % operator
+        elif (operator == '=' and isinstance(rhs, Constant)
+              and rhs.eval(self._args) is None):
+            if lhs is None:
+                return ' IS NULL'
+            return '%s IS NULL' % lhs.accept(self, contextrels)
         elif isinstance(rhs, Function) and rhs.name == 'IN':
             assert operator == '='
             operator = ' '
@@ -918,7 +925,7 @@
             return '%s%s'% (operator, rhs.accept(self, contextrels))
         return '%s%s%s'% (lhs.accept(self, contextrels), operator,
                           rhs.accept(self, contextrels))
-            
+
     def visit_mathexpression(self, mexpr, contextrels=None):
         """generate SQL for a mathematic expression"""
         lhs, rhs = mexpr.get_parts()
@@ -931,11 +938,11 @@
             pass
         return '(%s %s %s)'% (lhs.accept(self, contextrels), operator,
                               rhs.accept(self, contextrels))
-        
+
     def visit_function(self, func, contextrels=None):
         """generate SQL name for a function"""
         # function_description will check function is supported by the backend
-        sqlname = self.dbms_helper.func_sqlname(func.name) 
+        sqlname = self.dbms_helper.func_sqlname(func.name)
         return '%s(%s)' % (sqlname, ', '.join(c.accept(self, contextrels)
                                               for c in func.children))
 
@@ -963,7 +970,7 @@
                 value = value.encode(self.dbencoding)
             self._query_attrs[_id] = value
         return '%%(%s)s' % _id
-        
+
     def visit_variableref(self, variableref, contextrels=None):
         """get the sql name for a variable reference"""
         # use accept, .variable may be a variable or a columnalias
@@ -979,7 +986,7 @@
             self.add_table(table)
             return sql
         return colalias._q_sql
-    
+
     def visit_variable(self, variable, contextrels=None):
         """get the table name and sql string for a variable"""
         if contextrels is None and variable.name in self._state.done:
@@ -989,7 +996,7 @@
         self._state.done.add(variable.name)
         vtablename = None
         if contextrels is None and variable.name in self._varmap:
-            sql, vtablename = self._var_info(variable)            
+            sql, vtablename = self._var_info(variable)
         elif variable.stinfo['attrvar']:
             # attribute variable (systematically used in rhs of final
             # relation(s)), get table name and sql from any rhs relation
@@ -1043,7 +1050,7 @@
             # so nothing to return here
             pass
         return ''
-    
+
     def _var_info(self, var):
         # if current var or one of its attribute is selected , it *must*
         # appear in the toplevel's FROM even if we're currently visiting
@@ -1067,7 +1074,7 @@
             sql = '%s.%seid' % (table, SQL_PREFIX)
             self.add_table('%s%s AS %s' % (SQL_PREFIX, etype, table), table, scope=scope)
         return sql, table
-    
+
     def _inlined_var_sql(self, var, rtype):
         try:
             sql = self._varmap['%s.%s' % (var.name, rtype)]
@@ -1077,14 +1084,14 @@
             sql = '%s.%s%s' % (self._var_table(var), SQL_PREFIX, rtype)
             #self._state.done.add(var.name)
         return sql
-        
+
     def _linked_var_sql(self, variable, contextrels=None):
         if contextrels is None:
             try:
-                return self._varmap[variable.name]            
+                return self._varmap[variable.name]
             except KeyError:
                 pass
-        rel = (contextrels and contextrels.get(variable.name) or 
+        rel = (contextrels and contextrels.get(variable.name) or
                variable.stinfo.get('principal') or
                iter(variable.stinfo['rhsrelations']).next())
         linkedvar = rel.children[0].variable
@@ -1096,7 +1103,7 @@
         try:
             sql = self._varmap['%s.%s' % (linkedvar.name, rel.r_type)]
         except KeyError:
-            linkedvar.accept(self)            
+            linkedvar.accept(self)
             sql = '%s.%s%s' % (linkedvar._q_sqltable, SQL_PREFIX, rel.r_type)
         return sql
 
@@ -1107,7 +1114,7 @@
         self._state.count += 1
         self.add_table('%s AS %s' % (tablename, alias), alias)
         return alias
-        
+
     def add_table(self, table, key=None, scope=-1):
         if key is None:
             key = table
@@ -1115,7 +1122,7 @@
             return
         self._state.tables[key] = (len(self._state.actual_tables) - 1, table)
         self._state.actual_tables[scope].append(table)
-    
+
     def replace_tables_by_outer_join(self, substitute, lefttable, *tables):
         for table in tables:
             try:
@@ -1160,8 +1167,8 @@
         for table, outerexpr in self._state.outer_tables.iteritems():
             if outerexpr == oldalias:
                 self._state.outer_tables[table] = newalias
-        self._state.outer_tables[table] = newalias        
-        
+        self._state.outer_tables[table] = newalias
+
     def _var_table(self, var):
         var.accept(self)#.visit_variable(var)
         return var._q_sqltable
@@ -1173,7 +1180,7 @@
         assert not self.schema.rschema(relation.r_type).is_final(), relation.r_type
         rid = 'rel_%s%s' % (relation.r_type, self._state.count)
         # relation's table is belonging to the root scope if it is the principal
-        # table of one of it's variable and if that variable belong's to parent 
+        # table of one of it's variable and if that variable belong's to parent
         # scope
         for varref in relation.iget_nodes(VariableRef):
             var = varref.variable
@@ -1192,7 +1199,7 @@
         relation._q_sqltable = rid
         self._state.done.add(relation)
         return rid
-    
+
     def _fti_table(self, relation):
         if relation in self._state.done:
             try:
@@ -1203,7 +1210,7 @@
         alias = self.alias_and_add_table(self.dbms_helper.fti_table)
         relation._q_sqltable = alias
         return alias
-        
+
     def _varmap_table_scope(self, select, table):
         """since a varmap table may be used for multiple variable, its scope is
         the most outer scope of each variables
--- a/server/sqlutils.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/sqlutils.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """SQL utilities functions and classes.
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -111,13 +112,14 @@
         w(indexer.sql_drop_fti())
         w('')
     w(dropschema2sql(schema, prefix=SQL_PREFIX,
-                     skip_entities=skip_entities, skip_relations=skip_relations))
+                     skip_entities=skip_entities,
+                     skip_relations=skip_relations))
     return '\n'.join(output)
 
 try:
     from mx.DateTime import DateTimeType, DateTimeDeltaType
 except ImportError:
-    DateTimeType, DateTimeDeltaType = None
+    DateTimeType = DateTimeDeltaType = None
 
 class SQLAdapterMixIn(object):
     """Mixin for SQL data sources, getting a connection from a configuration
--- a/server/ssplanner.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/ssplanner.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """plan execution of rql queries on a single source
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -78,35 +79,35 @@
                 rel = newroot.add_type_restriction(var, possibletypes)
             stinfo['typerels'] = frozenset((rel,))
             stinfo['possibletypes'] = possibletypes
-        
+
 class SSPlanner(object):
     """SingleSourcePlanner: build execution plan for rql queries
 
     optimized for single source repositories
     """
-    
+
     def __init__(self, schema, rqlhelper):
         self.schema = schema
         self.rqlhelper = rqlhelper
 
     def build_plan(self, plan):
         """build an execution plan from a RQL query
-        
+
         do nothing here, dispatch according to the statement type
         """
         build_plan = getattr(self, 'build_%s_plan' % plan.rqlst.TYPE)
         for step in build_plan(plan, plan.rqlst):
             plan.add_step(step)
-    
+
     def build_select_plan(self, plan, rqlst):
         """build execution plan for a SELECT RQL query. Suppose only one source
         is available and so avoid work need for query decomposition among sources
-               
+
         the rqlst should not be tagged at this point.
         """
         plan.preprocess(rqlst)
         return (OneFetchStep(plan, rqlst, plan.session.repo.sources),)
-            
+
     def build_insert_plan(self, plan, rqlst):
         """get an execution plan from an INSERT RQL query"""
         # each variable in main variables is a new entity to insert
@@ -123,7 +124,7 @@
         step.children += self._compute_relation_steps(plan, rqlst.solutions,
                                                       rqlst.where, to_select)
         return (step,)
-        
+
     def _compute_relation_steps(self, plan, solutions, restriction, to_select):
         """handle the selection of relations for an insert query"""
         for edef, rdefs in to_select.items():
@@ -146,7 +147,7 @@
             step = RelationsStep(plan, edef, rdefs)
             step.children += self._select_plan(plan, select, solutions)
             yield step
-    
+
     def build_delete_plan(self, plan, rqlst):
         """get an execution plan from a DELETE RQL query"""
         # build a select query to fetch entities to delete
@@ -174,7 +175,7 @@
         if etype != 'Any':
             select.add_type_restriction(varref.variable, etype)
         return self._select_plan(plan, select, solutions)
-        
+
     def _sel_relation_steps(self, plan, solutions, restriction, relation):
         """handle the selection of relations for a delete query"""
         select = Select()
@@ -185,7 +186,7 @@
         if restriction is not None:
             select.add_restriction(restriction.copy(select))
         return self._select_plan(plan, select, solutions)
-    
+
     def build_set_plan(self, plan, rqlst):
         """get an execution plan from an SET RQL query"""
         select = Select()
@@ -222,12 +223,12 @@
         return (step,)
 
     # internal methods ########################################################
-    
+
     def _select_plan(self, plan, select, solutions):
         union = Union()
         union.append(select)
         select.clean_solutions(solutions)
-        add_types_restriction(self.schema, select)        
+        add_types_restriction(self.schema, select)
         self.rqlhelper.annotate(union)
         return self.build_select_plan(plan, union)
 
@@ -260,35 +261,35 @@
         self.limit = limit
         self.offset = offset or None
 
-        
+
 class Step(object):
     """base abstract class for execution step"""
     def __init__(self, plan):
         self.plan = plan
         self.children = []
-        
+
     def execute_child(self):
         assert len(self.children) == 1
         return self.children[0].execute()
-    
+
     def execute_children(self):
         for step in self.children:
             step.execute()
-        
+
     def execute(self):
         """execute this step and store partial (eg this step) results"""
         raise NotImplementedError()
-    
+
     def mytest_repr(self):
         """return a representation of this step suitable for test"""
         return (self.__class__.__name__,)
-    
+
     def test_repr(self):
         """return a representation of this step suitable for test"""
         return self.mytest_repr() + (
             [step.test_repr() for step in self.children],)
 
-        
+
 class OneFetchStep(LimitOffsetMixIn, Step):
     """step consisting in fetching data from sources and directly returning
     results
@@ -305,7 +306,7 @@
         for select in self.union.children:
             select.limit = limit
             select.offset = offset
-        
+
     def execute(self):
         """call .syntax_tree_search with the given syntax tree on each
         source for each solution
@@ -375,22 +376,22 @@
 
     relations values comes from the latest result, with one columns for
     each relation defined in self.r_defs
-    
+
     for one entity definition, we'll construct N entity, where N is the
     number of the latest result
     """
-    
+
     FINAL = 0
     RELATION = 1
     REVERSE_RELATION = 2
-    
+
     def __init__(self, plan, e_def, r_defs):
         Step.__init__(self, plan)
         # partial entity definition to expand
         self.e_def = e_def
         # definition of relations to complete
         self.r_defs = r_defs
-        
+
     def execute(self):
         """execute this step"""
         base_e_def = self.e_def
@@ -415,7 +416,7 @@
 
 class InsertStep(Step):
     """step consisting in inserting new entities / relations"""
-    
+
     def execute(self):
         """execute this step"""
         for step in self.children:
@@ -444,34 +445,34 @@
         pending |= actual
         for eid in actual:
             delete(session, eid)
-            
-    
+
+
 class DeleteRelationsStep(Step):
     """step consisting in deleting relations"""
 
     def __init__(self, plan, rtype):
         Step.__init__(self, plan)
         self.rtype = rtype
-        
+
     def execute(self):
         """execute this step"""
         session = self.plan.session
         delete = session.repo.glob_delete_relation
         for subj, obj in self.execute_child():
             delete(session, subj, self.rtype, obj)
-    
+
 
 class UpdateStep(Step):
     """step consisting in updating entities / adding relations from relations
     definitions and from results fetched in previous step
     """
-    
+
     def __init__(self, plan, attribute_relations, relations, selected_index):
         Step.__init__(self, plan)
         self.attribute_relations = attribute_relations
         self.relations = relations
         self.selected_index = selected_index
-        
+
     def execute(self):
         """execute this step"""
         plan = self.plan
--- a/server/test/data/config1/application_hooks.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/data/config1/application_hooks.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,6 +2,7 @@
 
  Copyright (c) 2003-2007 LOGILAB S.A. (Paris, FRANCE).
  http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 HOOKS = {"after_add_relation" : {"concerned_by" : [lambda: None]}}
--- a/server/test/data/config2/application_hooks.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/data/config2/application_hooks.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,6 +2,7 @@
 
  Copyright (c) 2003-2007 LOGILAB S.A. (Paris, FRANCE).
  http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 HOOKS = {"after_delete_relation" : {"todo_by" : [lambda: 1]}}
--- a/server/test/data/extern_mapping.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/data/extern_mapping.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,2 +1,9 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 support_entities = {'Card': True, 'Affaire': True, 'State': True}
 support_relations = {'in_state': True, 'documented_by': True, 'multisource_inlined_rel': True}
--- a/server/test/data/hooks.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/data/hooks.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from cubicweb.server.hooksmanager import SystemHook
 
 CALLED_EVENTS = {}
--- a/server/test/data/migration/postcreate.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/data/migration/postcreate.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""cubicweb post creation script, set note's workflow"""
+"""cubicweb post creation script, set note's workflow
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 todoeid = add_state(u'todo', 'Note', initial=True)
 doneeid = add_state(u'done', 'Note')
--- a/server/test/data/migrschema/Affaire.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/data/migrschema/Affaire.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 class Affaire(EntityType):
     permissions = {
@@ -6,7 +13,7 @@
         'update': ('managers', 'owners', ERQLExpression('X concerne S, S owned_by U')),
         'delete': ('managers', 'owners', ERQLExpression('X concerne S, S owned_by U')),
         }
-    
+
     ref = String(fulltextindexed=True, indexed=True,
                  constraints=[SizeConstraint(16)])
     sujet = String(fulltextindexed=True,
@@ -18,4 +25,4 @@
         'add':    ('managers', RRQLExpression('U has_update_permission S')),
         'delete': ('managers', RRQLExpression('O owned_by U')),
         }
-    
+
--- a/server/test/data/migrschema/Folder2.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/data/migrschema/Folder2.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from cubicweb.schema import format_constraint
 
 class Folder2(MetaUserEntityType):
@@ -22,4 +29,4 @@
     # is_about has been renamed into filed_under
     #//* is_about Folder
     #* filed_under Folder
-    
+
--- a/server/test/data/migrschema/Note.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/data/migrschema/Note.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,13 +1,20 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 class Note(EntityType):
-    
+
     permissions = {'read':   ('managers', 'users', 'guests',),
                    'update': ('managers', 'owners',),
                    'delete': ('managers', ),
-                   'add':    ('managers', 
+                   'add':    ('managers',
                               ERQLExpression('X ecrit_part PE, U in_group G, '
                                              'PE require_permission P, P name "add_note", '
                                              'P require_group G'),)}
-    
+
     date = Datetime()
     type = String(maxsize=1)
     whatever = Int()
@@ -19,7 +26,7 @@
 class ecrit_par(RelationType):
     permissions = {'read':   ('managers', 'users', 'guests',),
                    'delete': ('managers', ),
-                   'add':    ('managers', 
+                   'add':    ('managers',
                               RRQLExpression('O require_permission P, P name "add_note", '
                                              'U in_group G, P require_group G'),)
                    }
--- a/server/test/data/schema/Affaire.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/data/schema/Affaire.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,14 +1,21 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from cubicweb.schema import format_constraint
 
 class Affaire(WorkflowableEntityType):
     permissions = {
-        'read':   ('managers', 
+        'read':   ('managers',
                    ERQLExpression('X owned_by U'), ERQLExpression('X concerne S?, S owned_by U')),
         'add':    ('managers', ERQLExpression('X concerne S, S owned_by U')),
         'update': ('managers', 'owners', ERQLExpression('X in_state S, S name in ("pitetre", "en cours")')),
         'delete': ('managers', 'owners', ERQLExpression('X concerne S, S owned_by U')),
         }
-    
+
     ref = String(fulltextindexed=True, indexed=True,
                  constraints=[SizeConstraint(16)])
     sujet = String(fulltextindexed=True,
@@ -23,12 +30,12 @@
 
     depends_on = SubjectRelation('Affaire')
     require_permission = SubjectRelation('CWPermission')
-    
+
 class concerne(RelationType):
     permissions = {
         'read':   ('managers', 'users', 'guests'),
         'add':    ('managers', RRQLExpression('U has_update_permission S')),
         'delete': ('managers', RRQLExpression('O owned_by U')),
         }
-    
 
+
--- a/server/test/data/schema/Societe.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/data/schema/Societe.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 class Societe(EntityType):
     permissions = {
         'read': ('managers', 'users', 'guests'),
@@ -5,10 +12,10 @@
         'delete': ('managers', 'owners', ERQLExpression('U login L, X nom L')),
         'add': ('managers', 'users',)
         }
-    
+
     nom  = String(maxsize=64, fulltextindexed=True)
     web  = String(maxsize=128)
-    type  = String(maxsize=128) # attribute in common with Note 
+    type  = String(maxsize=128) # attribute in common with Note
     tel  = Int()
     fax  = Int()
     rncs = String(maxsize=128)
--- a/server/test/data/schema/custom.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/data/schema/custom.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 
 class test(AttributeRelationType):
--- a/server/test/data/schema/note.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/data/schema/note.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 class para(AttributeRelationType):
     permissions = {
@@ -11,7 +18,7 @@
     object = 'State'
     cardinality = '1*'
     constraints=[RQLConstraint('S is ET, O state_of ET')]
-    
+
 class wf_info_for(RelationDefinition):
     subject = 'TrInfo'
     object = 'Note'
--- a/server/test/data/site_erudi.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/data/site_erudi.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from logilab.common.adbh import FunctionDescr
 from rql.utils import register_function
 
--- a/server/test/data/sources_multi	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/data/sources_multi	Wed Jun 03 09:15:20 2009 +0200
@@ -14,6 +14,7 @@
 cubicweb-user = admin
 cubicweb-password = gingkow
 mapping-file = extern_mapping.py
+base-url=http://extern.org/
 
 [extern-multi]
 adapter = pyrorql
--- a/server/test/unittest_checkintegrity.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/unittest_checkintegrity.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 import sys
 from StringIO import StringIO
 from logilab.common.testlib import TestCase, unittest_main
@@ -17,6 +24,6 @@
         finally:
             sys.stderr = sys.__stderr__
             sys.stdout = sys.__stdout__
-        
+
 if __name__ == '__main__':
     unittest_main()
--- a/server/test/unittest_config.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/unittest_config.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""tests for server config"""
+"""tests for server config
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 from os.path import join, dirname
 
@@ -13,7 +19,7 @@
             @staticmethod
             def registry_objects(registry):
                 return []
-            
+
         cfg1 = TestServerConfiguration('data/config1')
         cfg1.bootstrap_cubes()
         cfg2 = TestServerConfiguration('data/config2')
@@ -21,7 +27,7 @@
         self.failIf(cfg1.load_hooks(vreg) is cfg2.load_hooks(vreg))
         self.failUnless('after_add_relation' in cfg1.load_hooks(vreg))
         self.failUnless('after_delete_relation' in cfg2.load_hooks(vreg))
-        
+
 
 if __name__ == '__main__':
     unittest_main()
--- a/server/test/unittest_hookhelper.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/unittest_hookhelper.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,5 +1,11 @@
 # -*- coding: utf-8 -*-
-"""unit/functional tests for cubicweb.server.hookhelper"""
+"""unit/functional tests for cubicweb.server.hookhelper
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 from logilab.common.testlib import unittest_main
 from cubicweb.devtools.apptest import RepositoryBasedTC
--- a/server/test/unittest_hooks.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/unittest_hooks.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,6 +2,7 @@
 """functional tests for core hooks
 
 note: most schemahooks.py hooks are actually tested in unittest_migrations.py
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 from logilab.common.testlib import TestCase, unittest_main
@@ -454,6 +455,23 @@
             self.failIf(self.schema['Affaire'].has_unique_values('sujet'))
             self.failIf(self.index_exists('Affaire', 'sujet', unique=True))
 
+    def test_required_change_1(self):
+        self.execute('SET DEF cardinality "?1" '
+                     'WHERE DEF relation_type RT, DEF from_entity E,'
+                     'RT name "nom", E name "Personne"')
+        self.commit()
+        # should now be able to add personne without nom
+        self.execute('INSERT Personne X')
+        self.commit()
+
+    def test_required_change_2(self):
+        self.execute('SET DEF cardinality "11" '
+                     'WHERE DEF relation_type RT, DEF from_entity E,'
+                     'RT name "prenom", E name "Personne"')
+        self.commit()
+        # should not be able anymore to add personne without prenom
+        self.assertRaises(ValidationError, self.execute, 'INSERT Personne X: X nom "toto"')
+
 
 class WorkflowHooksTC(RepositoryBasedTC):
 
--- a/server/test/unittest_hooksmanager.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/unittest_hooksmanager.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,5 @@
 """unit tests for the hooks manager
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 from logilab.common.testlib import TestCase, unittest_main
@@ -16,7 +17,7 @@
 class HooksManagerTC(TestCase):
     args = (None,)
     kwargs = {'a': 1}
-    
+
     def setUp(self):
         """ called before each test from this class """
         self.o = HooksManager(schema)
@@ -32,22 +33,22 @@
                           self.o.register_hook, self._hook, 'server_startup', 'CWEType')
         self.assertRaises(AssertionError,
                           self.o.register_hook, self._hook, 'server_shutdown', 'CWEType')
-        
+
     def test_register_hook1(self):
         self.o.register_hook(self._hook, 'before_add_entity')
         self.o.register_hook(self._hook, 'before_delete_entity', 'Personne')
         self._test_called_hooks()
-        
+
     def test_register_hook2(self):
         self.o.register_hook(self._hook, 'before_add_entity', '')
         self.o.register_hook(self._hook, 'before_delete_entity', 'Personne')
         self._test_called_hooks()
-        
+
     def test_register_hook3(self):
         self.o.register_hook(self._hook, 'before_add_entity', None)
         self.o.register_hook(self._hook, 'before_delete_entity', 'Personne')
         self._test_called_hooks()
-        
+
     def test_register_hooks(self):
         self.o.register_hooks({'before_add_entity' : {'': [self._hook]},
                                'before_delete_entity' : {'Personne': [self._hook]},
@@ -62,7 +63,7 @@
         self.o.unregister_hook(self._hook, 'after_delete_entity', 'Personne')
         # no hook should be called there
         self.o.call_hooks('after_delete_entity', 'Personne')
-        
+
 
     def _test_called_hooks(self):
         self.assertRaises(HookCalled,
@@ -102,9 +103,9 @@
                              'before_add_relation', 'concerne')
         self.assertEquals(self.called, [])
         self.o.call_hooks('before_add_relation', 'concerne', 'USER',
-                          1, 'concerne', 2)        
+                          1, 'concerne', 2)
         self.assertEquals(self.called, [(1, 'concerne', 2)])
-        
+
     def test_after_add_relation(self):
         """make sure after_xxx_relation hooks are deferred"""
         self.o.register_hook(self._after_relation_hook,
@@ -115,14 +116,14 @@
         self.o.call_hooks('after_add_relation', 'concerne', 'USER',
                           3, 'concerne', 4)
         self.assertEquals(self.called, [(1, 'concerne', 2), (3, 'concerne', 4)])
-    
+
     def test_before_delete_relation(self):
         """make sure before_xxx_relation hooks are called directly"""
         self.o.register_hook(self._before_relation_hook,
                              'before_delete_relation', 'concerne')
         self.assertEquals(self.called, [])
         self.o.call_hooks('before_delete_relation', 'concerne', 'USER',
-                          1, 'concerne', 2)        
+                          1, 'concerne', 2)
         self.assertEquals(self.called, [(1, 'concerne', 2)])
 
     def test_after_delete_relation(self):
@@ -166,7 +167,7 @@
     schema = schema # set for actual hooks at registration time
     events = ('whatever', 'another')
     accepts = ('Societe', 'Division')
-    
+
 class HookTC(RepositoryBasedTC):
     def test_inheritance(self):
         self.assertEquals(list(MyHook.register_to()),
--- a/server/test/unittest_ldapuser.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/unittest_ldapuser.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""cubicweb.server.sources.ldapusers unit and functional tests"""
+"""cubicweb.server.sources.ldapusers unit and functional tests
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 from logilab.common.testlib import TestCase, unittest_main, mock_object
 from cubicweb.devtools import init_test_database, TestServerConfiguration
@@ -34,14 +40,14 @@
 
 class LDAPUserSourceTC(RepositoryBasedTC):
     repo, cnx = repo, cnx
-    
+
     def patch_authenticate(self):
         self._orig_authenticate = LDAPUserSource.authenticate
         LDAPUserSource.authenticate = nopwd_authenticate
 
     def setUp(self):
         self._prepare()
-        # XXX: need this first query else we get 'database is locked' from 
+        # XXX: need this first query else we get 'database is locked' from
         # sqlite since it doesn't support multiple connections on the same
         # database
         # so doing, ldap inserted users don't get removed between each test
@@ -50,21 +56,21 @@
         # check we get some users from ldap
         self.assert_(len(rset) > 1)
         self.maxeid = self.execute('Any MAX(X)')[0][0]
-        
+
     def tearDown(self):
         if hasattr(self, '_orig_authenticate'):
             LDAPUserSource.authenticate = self._orig_authenticate
         RepositoryBasedTC.tearDown(self)
-            
+
     def test_authenticate(self):
         source = self.repo.sources_by_uri['ldapuser']
         self.assertRaises(AuthenticationError,
                           source.authenticate, self.session, 'toto', 'toto')
-        
+
     def test_synchronize(self):
         source = self.repo.sources_by_uri['ldapuser']
         source.synchronize()
-        
+
     def test_base(self):
         # check a known one
         e = self.execute('CWUser X WHERE X login "syt"').get_entity(0, 0)
@@ -139,7 +145,7 @@
     def test_or(self):
         rset = self.execute('DISTINCT Any X WHERE X login "syt" OR (X in_group G, G name "managers")')
         self.assertEquals(len(rset), 2, rset.rows) # syt + admin
-        
+
     def test_nonregr_set_owned_by(self):
         # test that when a user coming from ldap is triggering a transition
         # the related TrInfo has correct owner information
@@ -175,7 +181,7 @@
     def test_multiple_entities_from_different_sources(self):
         self.create_user('cochon')
         self.failUnless(self.execute('Any X,Y WHERE X login "syt", Y login "cochon"'))
-        
+
     def test_exists1(self):
         self.add_entity('CWGroup', name=u'bougloup1')
         self.add_entity('CWGroup', name=u'bougloup2')
@@ -241,11 +247,11 @@
         self.assertEquals(sorted(rset.rows), [['guests', 'cochon'],
                                               ['users', 'cochon'],
                                               ['users', 'syt']])
-        
+
     def test_cd_restriction(self):
         rset = self.execute('CWUser X WHERE X creation_date > "2009-02-01"')
         self.assertEquals(len(rset), 2) # admin/anon but no ldap user since it doesn't support creation_date
-        
+
     def test_union(self):
         afeids = self.execute('State X')
         ueids = self.execute('CWUser X')
@@ -257,21 +263,21 @@
         self.create_user('iaminguestsgrouponly', groups=('guests',))
         cnx = self.login('iaminguestsgrouponly')
         return cnx.cursor()
-    
+
     def test_security1(self):
         cu = self._init_security_test()
         rset = cu.execute('Any X WHERE X login "syt"')
         self.assertEquals(rset.rows, [])
         rset = cu.execute('Any X WHERE X login "iaminguestsgrouponly"')
         self.assertEquals(len(rset.rows), 1)
-    
+
     def test_security2(self):
         cu = self._init_security_test()
         rset = cu.execute('Any X WHERE X has_text "syt"')
         self.assertEquals(rset.rows, [])
         rset = cu.execute('Any X WHERE X has_text "iaminguestsgrouponly"')
         self.assertEquals(len(rset.rows), 1)
-    
+
     def test_security3(self):
         cu = self._init_security_test()
         rset = cu.execute('Any F WHERE X has_text "syt", X firstname F')
@@ -298,13 +304,13 @@
         emaileid = self.execute('INSERT EmailAddress X: X address "toto@logilab.org"')[0][0]
         self.execute('Any X,AA WHERE X use_email Y, Y eid %(x)s, X modification_date AA',
                      {'x': emaileid})
-        
+
     def test_nonregr5(self):
         # original jpl query:
         # Any X, NOW - CD, P WHERE P is Project, U interested_in P, U is CWUser, U login "sthenault", X concerns P, X creation_date CD ORDERBY CD DESC LIMIT 5
         rql = 'Any X, NOW - CD, P ORDERBY CD DESC LIMIT 5 WHERE P bookmarked_by U, U login "%s", P is X, X creation_date CD' % self.session.user.login
         self.execute(rql, )#{'x': })
-        
+
     def test_nonregr6(self):
         self.execute('Any B,U,UL GROUPBY B,U,UL WHERE B created_by U?, B is File '
                      'WITH U,UL BEING (Any U,UL WHERE ME eid %(x)s, (EXISTS(U identity ME) '
@@ -350,33 +356,33 @@
 
 class RQL2LDAPFilterTC(RQLGeneratorTC):
     schema = repo.schema
-    
+
     def setUp(self):
         RQLGeneratorTC.setUp(self)
         ldapsource = repo.sources[-1]
         self.pool = repo._get_pool()
         session = mock_object(pool=self.pool)
         self.o = RQL2LDAPFilter(ldapsource, session)
-        
+
     def tearDown(self):
         repo._free_pool(self.pool)
         RQLGeneratorTC.tearDown(self)
-        
+
     def test_base(self):
         rqlst = self._prepare('CWUser X WHERE X login "toto"').children[0]
         self.assertEquals(self.o.generate(rqlst, 'X')[1],
                           '(&(objectClass=top)(objectClass=posixAccount)(uid=toto))')
-        
+
     def test_kwargs(self):
         rqlst = self._prepare('CWUser X WHERE X login %(x)s').children[0]
         self.o._args = {'x': "toto"}
         self.assertEquals(self.o.generate(rqlst, 'X')[1],
                           '(&(objectClass=top)(objectClass=posixAccount)(uid=toto))')
-        
+
     def test_get_attr(self):
         rqlst = self._prepare('Any X WHERE E firstname X, E eid 12').children[0]
         self.assertRaises(UnknownEid, self.o.generate, rqlst, 'E')
-        
-        
+
+
 if __name__ == '__main__':
     unittest_main()
--- a/server/test/unittest_migractions.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/unittest_migractions.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,5 @@
 """unit tests for module cubicweb.server.migractions
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 from datetime import date
--- a/server/test/unittest_msplanner.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/unittest_msplanner.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from logilab.common.decorators import clear_cache
 from cubicweb.devtools import init_test_database
 from cubicweb.devtools.repotest import BasePlannerTC, do_monkey_patch, undo_monkey_patch, test_plan
--- a/server/test/unittest_multisources.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/unittest_multisources.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from os.path import dirname, join, abspath
 from datetime import datetime, timedelta
 
@@ -77,7 +84,7 @@
                            'type': u'Card', 'extid': None})
         externent = rset.get_entity(3, 0)
         metainf = externent.metainformation()
-        self.assertEquals(metainf['source'], {'adapter': 'pyrorql', 'uri': 'extern'})
+        self.assertEquals(metainf['source'], {'adapter': 'pyrorql', 'base-url': 'http://extern.org/', 'uri': 'extern'})
         self.assertEquals(metainf['type'], 'Card')
         self.assert_(metainf['extid'])
         etype = self.execute('Any ETN WHERE X is ET, ET name ETN, X eid %(x)s',
@@ -149,7 +156,7 @@
         self.execute('Any X ORDERBY DUMB_SORT(RF) WHERE X title RF')
 
     def test_in_eid(self):
-        iec1 = self.repo.extid2eid(self.repo.sources_by_uri['extern'], ec1,
+        iec1 = self.repo.extid2eid(self.repo.sources_by_uri['extern'], str(ec1),
                                    'Card', self.session)
         rset = self.execute('Any X WHERE X eid IN (%s, %s)' % (iec1, self.ic1))
         self.assertEquals(sorted(r[0] for r in rset.rows), sorted([iec1, self.ic1]))
@@ -235,9 +242,21 @@
         states.remove((aff1stateeid, aff1statename))
         notstates = set(tuple(x) for x in self.execute('Any S,SN WHERE S is State, S name SN, NOT X in_state S, X eid %(x)s',
                                                        {'x': aff1}, 'x'))
-        self.set_debug(False)
         self.assertSetEquals(notstates, states)
 
+    def test_absolute_url_base_url(self):
+        ceid = cu.execute('INSERT Card X: X title "without wikiid to get eid based url"')[0][0]
+        cnx2.commit()
+        lc = self.execute('Card X WHERE X title "without wikiid to get eid based url"').get_entity(0, 0)
+        self.assertEquals(lc.absolute_url(), 'http://extern.org/card/eid/%s' % ceid)
+
+    def test_absolute_url_no_base_url(self):
+        cu = cnx3.cursor()
+        ceid = cu.execute('INSERT Card X: X title "without wikiid to get eid based url"')[0][0]
+        cnx3.commit()
+        lc = self.execute('Card X WHERE X title "without wikiid to get eid based url"').get_entity(0, 0)
+        self.assertEquals(lc.absolute_url(), 'http://testing.fr/cubicweb/card/eid/%s' % lc.eid)
+
     def test_nonregr1(self):
         ueid = self.session.user.eid
         affaire = self.execute('Affaire X WHERE X ref "AFFREF"').get_entity(0, 0)
@@ -251,7 +270,6 @@
         self.assertEquals(len(rset), 1)
         self.assertEquals(rset.rows[0], [self.session.user.eid])
 
-
     def test_nonregr3(self):
         self.execute('DELETE Card X WHERE X eid %(x)s, NOT X multisource_inlined_rel Y', {'x': self.ic1})
 
--- a/server/test/unittest_querier.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/unittest_querier.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,12 +1,13 @@
 # -*- coding: iso-8859-1 -*-
 """unit tests for modules cubicweb.server.querier and cubicweb.server.querier_steps
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 from datetime import date, datetime
 
 from logilab.common.testlib import TestCase, unittest_main
 from rql import BadRQLQuery, RQLSyntaxError
 
-from cubicweb import QueryError, Unauthorized
+from cubicweb import QueryError, Unauthorized, Binary
 from cubicweb.server.sqlutils import SQL_PREFIX
 from cubicweb.server.utils import crypt_password
 from cubicweb.server.sources.native import make_schema
@@ -208,6 +209,13 @@
         # should return an empty result set
         self.failIf(self.execute('Any X WHERE X eid 99999999'))
 
+    def test_bytes_storage(self):
+        feid = self.execute('INSERT File X: X name "foo.pdf", X data_format "text/plain", X data %(data)s',
+                            {'data': Binary("xxx")})[0][0]
+        fdata = self.execute('Any D WHERE X data D, X eid %(x)s', {'x': feid}, 'x')[0][0]
+        self.assertIsInstance(fdata, Binary)
+        self.assertEquals(fdata.getvalue(), 'xxx')
+
     # selection queries tests #################################################
 
     def test_select_1(self):
@@ -470,7 +478,7 @@
         self.assertEquals(rset.rows[0][0], self.ueid)
 
     def test_select_complex_sort(self):
-        self.skip('retry me once http://www.sqlite.org/cvstrac/tktview?tn=3773 is fixed')
+        """need sqlite including http://www.sqlite.org/cvstrac/tktview?tn=3773 fix"""
         rset = self.execute('Any X ORDERBY X,D LIMIT 5 WHERE X creation_date D')
         result = rset.rows
         result.sort()
@@ -1073,7 +1081,7 @@
         cursor = self.pool['system']
         cursor.execute("SELECT %supassword from %sCWUser WHERE %slogin='bob'"
                        % (SQL_PREFIX, SQL_PREFIX, SQL_PREFIX))
-        passwd = cursor.fetchone()[0].getvalue()
+        passwd = str(cursor.fetchone()[0])
         self.assertEquals(passwd, crypt_password('toto', passwd[:2]))
         rset = self.execute("Any X WHERE X is CWUser, X login 'bob', X upassword '%s'" % passwd)
         self.assertEquals(len(rset.rows), 1)
@@ -1087,7 +1095,7 @@
                             {'pwd': 'tutu'})
         cursor.execute("SELECT %supassword from %sCWUser WHERE %slogin='bob'"
                        % (SQL_PREFIX, SQL_PREFIX, SQL_PREFIX))
-        passwd = cursor.fetchone()[0].getvalue()
+        passwd = str(cursor.fetchone()[0])
         self.assertEquals(passwd, crypt_password('tutu', passwd[:2]))
         rset = self.execute("Any X WHERE X is CWUser, X login 'bob', X upassword '%s'" % passwd)
         self.assertEquals(len(rset.rows), 1)
@@ -1212,8 +1220,9 @@
 
         cause: old variable ref inserted into a fresh rqlst copy
         (in RQLSpliter._complex_select_plan)
+
+        need sqlite including http://www.sqlite.org/cvstrac/tktview?tn=3773 fix
         """
-        self.skip('retry me once http://www.sqlite.org/cvstrac/tktview?tn=3773 is fixed')
         self.execute('Any X ORDERBY D DESC WHERE X creation_date D')
 
     def test_nonregr_extra_joins(self):
--- a/server/test/unittest_repository.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/unittest_repository.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,5 +1,11 @@
 # -*- coding: iso-8859-1 -*-
-"""unit tests for module cubicweb.server.repository"""
+"""unit tests for module cubicweb.server.repository
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 import os
 import sys
@@ -199,6 +205,23 @@
     def test_transaction_interleaved(self):
         self.skip('implement me')
 
+    def test_close_wait_processing_request(self):
+        repo = self.repo
+        cnxid = repo.connect(*self.default_user_password())
+        repo.execute(cnxid, 'INSERT CWUser X: X login "toto", X upassword "tutu", X in_group G WHERE G name "users"')
+        repo.commit(cnxid)
+        # close has to be in the thread due to sqlite limitations
+        def close_in_a_few_moment():
+            time.sleep(0.1)
+            repo.close(cnxid)
+        t = threading.Thread(target=close_in_a_few_moment)
+        t.start()
+        try:
+            repo.execute(cnxid, 'DELETE CWUser X WHERE X login "toto"')
+            repo.commit(cnxid)
+        finally:
+            t.join()
+
     def test_initial_schema(self):
         schema = self.repo.schema
         # check order of attributes is respected
@@ -238,37 +261,36 @@
     def test_pyro(self):
         import Pyro
         Pyro.config.PYRO_MULTITHREADED = 0
-        lock = threading.Lock()
+        done = []
         # the client part has to be in the thread due to sqlite limitations
-        t = threading.Thread(target=self._pyro_client, args=(lock,))
+        t = threading.Thread(target=self._pyro_client, args=(done,))
         try:
             daemon = self.repo.pyro_register()
             t.start()
-            # connection
-            daemon.handleRequests(1.0)
-            daemon.handleRequests(1.0)
-            daemon.handleRequests(1.0)
-            # get schema
-            daemon.handleRequests(1.0)
-            # execute
-            daemon.handleRequests(1.0)
-            t.join()
+            while not done:
+                daemon.handleRequests(1.0)
+            t.join(1)
+            if t.isAlive():
+                self.fail('something went wrong, thread still alive')
         finally:
             repository.pyro_unregister(self.repo.config)
 
-    def _pyro_client(self, lock):
+    def _pyro_client(self, done):
         cnx = connect(self.repo.config.appid, u'admin', 'gingkow')
         # check we can get the schema
         schema = cnx.get_schema()
         self.assertEquals(schema.__hashmode__, None)
-        rset = cnx.cursor().execute('Any U,G WHERE U in_group G')
-
+        cu = cnx.cursor()
+        rset = cu.execute('Any U,G WHERE U in_group G')
+        cnx.close()
+        done.append(True)
 
     def test_internal_api(self):
         repo = self.repo
         cnxid = repo.connect(*self.default_user_password())
         session = repo._get_session(cnxid, setpool=True)
-        self.assertEquals(repo.type_and_source_from_eid(1, session), ('CWGroup', 'system', None))
+        self.assertEquals(repo.type_and_source_from_eid(1, session),
+                          ('CWGroup', 'system', None))
         self.assertEquals(repo.type_from_eid(1, session), 'CWGroup')
         self.assertEquals(repo.source_from_eid(1, session).uri, 'system')
         self.assertEquals(repo.eid2extid(repo.system_source, 1, session), None)
--- a/server/test/unittest_rql2sql.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/unittest_rql2sql.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,11 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
+
 """unit tests for module cubicweb.server.sources.rql2sql"""
 
 import sys
@@ -1211,6 +1219,14 @@
 WHERE rel_in_group0.eid_from=T00.x AND rel_in_group0.eid_to=G.cw_eid''',
                     varmap={'X': 'T00.x', 'X.login': 'T00.l'})
 
+    def test_is_null_transform(self):
+        union = self._prepare('Any X WHERE X login %(login)s')
+        r, args = self.o.generate(union, {'login': None})
+        self.assertLinesEquals((r % args).strip(),
+                               '''SELECT X.cw_eid
+FROM cw_CWUser AS X
+WHERE X.cw_login IS NULL''')
+
     def test_parser_parse(self):
         for t in self._parse(PARSER):
             yield t
--- a/server/test/unittest_rqlannotation.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/unittest_rqlannotation.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,5 +1,6 @@
 # -*- coding: iso-8859-1 -*-
 """unit tests for modules cubicweb.server.rqlannotation
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 from cubicweb.devtools import init_test_database
@@ -9,15 +10,15 @@
 
 class SQLGenAnnotatorTC(BaseQuerierTC):
     repo = repo
-    
+
     def get_max_eid(self):
         # no need for cleanup here
         return None
     def cleanup(self):
         # no need for cleanup here
         pass
-                
-    def test_0_1(self):        
+
+    def test_0_1(self):
         rqlst = self._prepare('Any SEN,RN,OEN WHERE X from_entity SE, SE eid 44, X relation_type R, R eid 139, X to_entity OE, OE eid 42, R name RN, SE name SEN, OE name OEN')
         self.assertEquals(rqlst.defined_vars['SE']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['OE']._q_invariant, False)
@@ -25,13 +26,13 @@
         self.assertEquals(rqlst.defined_vars['SE'].stinfo['attrvar'], None)
         self.assertEquals(rqlst.defined_vars['OE'].stinfo['attrvar'], None)
         self.assertEquals(rqlst.defined_vars['R'].stinfo['attrvar'], None)
-        
-    def test_0_2(self):        
+
+    def test_0_2(self):
         rqlst = self._prepare('Any O WHERE NOT S ecrit_par O, S eid 1, S inline1 P, O inline2 P')
         self.assertEquals(rqlst.defined_vars['P']._q_invariant, True)
         self.assertEquals(rqlst.defined_vars['O'].stinfo['attrvar'], None)
 
-    def test_0_4(self):        
+    def test_0_4(self):
         rqlst = self._prepare('Any A,B,C WHERE A eid 12,A comment B, A ?wf_info_for C')
         self.assertEquals(rqlst.defined_vars['A']._q_invariant, False)
         self.assert_(rqlst.defined_vars['B'].stinfo['attrvar'])
@@ -40,28 +41,28 @@
                                       {'A': 'TrInfo', 'B': 'String', 'C': 'CWUser'},
                                       {'A': 'TrInfo', 'B': 'String', 'C': 'Note'}])
 
-    def test_0_5(self):        
+    def test_0_5(self):
         rqlst = self._prepare('Any P WHERE N ecrit_par P, N eid 0')
         self.assertEquals(rqlst.defined_vars['N']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['P']._q_invariant, True)
 
-    def test_0_6(self):        
+    def test_0_6(self):
         rqlst = self._prepare('Any P WHERE NOT N ecrit_par P, N eid 512')
         self.assertEquals(rqlst.defined_vars['P']._q_invariant, False)
 
-    def test_0_7(self):        
+    def test_0_7(self):
         rqlst = self._prepare('Personne X,Y where X nom NX, Y nom NX, X eid XE, not Y eid XE')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, False)
         self.assert_(rqlst.defined_vars['XE'].stinfo['attrvar'])
 
-    def test_0_8(self):        
+    def test_0_8(self):
         rqlst = self._prepare('Any P WHERE X eid 0, NOT X connait P')
         self.assertEquals(rqlst.defined_vars['P']._q_invariant, False)
         #self.assertEquals(rqlst.defined_vars['X']._q_invariant, True)
         self.assertEquals(len(rqlst.solutions), 1, rqlst.solutions)
 
-    def test_0_10(self):        
+    def test_0_10(self):
         rqlst = self._prepare('Any X WHERE X concerne Y, Y is Note')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, True)
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, False)
@@ -71,78 +72,78 @@
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, True)
 
-    def test_0_12(self):        
+    def test_0_12(self):
         rqlst = self._prepare('Personne P WHERE P concerne A, A concerne S, S nom "Logilab"')
         self.assertEquals(rqlst.defined_vars['P']._q_invariant, True)
         self.assertEquals(rqlst.defined_vars['A']._q_invariant, True)
         self.assertEquals(rqlst.defined_vars['S']._q_invariant, False)
-        
+
     def test_1_0(self):
         rqlst = self._prepare('Any X,Y WHERE X created_by Y, X eid 5, NOT Y eid 6')
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, True)
-                
+
     def test_1_1(self):
         rqlst = self._prepare('Any X,Y WHERE X created_by Y, X eid 5, NOT Y eid IN (6,7)')
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, True)
-                
+
     def test_2(self):
         rqlst = self._prepare('Any X WHERE X identity Y, Y eid 1')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
-        
+
     def test_7(self):
         rqlst = self._prepare('Personne X,Y where X nom NX, Y nom NX, X eid XE, not Y eid XE')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, False)
-        
+
     def test_optional_inlined(self):
         rqlst = self._prepare('Any X,S where X from_state S?')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['S']._q_invariant, True)
-                
+
     def test_optional_inlined_2(self):
         rqlst = self._prepare('Any N,A WHERE N? inline1 A')
         self.assertEquals(rqlst.defined_vars['N']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['A']._q_invariant, False)
-        
+
     def test_optional_1(self):
         rqlst = self._prepare('Any X,S WHERE X travaille S?')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['S']._q_invariant, True)
-        
+
     def test_greater_eid(self):
         rqlst = self._prepare('Any X WHERE X eid > 5')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, True)
-        
+
     def test_greater_eid_typed(self):
         rqlst = self._prepare('Any X WHERE X eid > 5, X is Note')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, True)
-        
+
     def test_max_eid(self):
         rqlst = self._prepare('Any MAX(X)')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, True)
-        
+
     def test_max_eid_typed(self):
         rqlst = self._prepare('Any MAX(X) WHERE X is Note')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
-        
+
     def test_all_entities(self):
         rqlst = self._prepare('Any X')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, True)
-        
+
     def test_all_typed_entity(self):
         rqlst = self._prepare('Any X WHERE X is Note')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
-        
+
     def test_has_text_1(self):
         rqlst = self._prepare('Any X WHERE X has_text "toto tata"')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, True)
         self.assertEquals(rqlst.defined_vars['X'].stinfo['principal'].r_type, 'has_text')
-        
+
     def test_has_text_2(self):
         rqlst = self._prepare('Any X WHERE X is Personne, X has_text "coucou"')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, True)
         self.assertEquals(rqlst.defined_vars['X'].stinfo['principal'].r_type, 'has_text')
-        
+
     def test_not_relation_1(self):
         # P can't be invariant since deambiguification caused by "NOT X require_permission P"
         # is not considered by generated sql (NOT EXISTS(...))
@@ -150,31 +151,31 @@
         self.assertEquals(rqlst.defined_vars['P']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['G']._q_invariant, True)
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, True)
-        
+
     def test_not_relation_2(self):
         rqlst = self._prepare('TrInfo X WHERE X eid 2, NOT X from_state Y, Y is State')
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, True)
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
-                
+
     def test_not_relation_3(self):
         rqlst = self._prepare('Any X, Y WHERE X eid 1, Y eid in (2, 3)')
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, False)
-                
+
     def test_not_relation_4_1(self):
         rqlst = self._prepare('Note X WHERE NOT Y evaluee X')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, True)
-        
+
     def test_not_relation_4_2(self):
         rqlst = self._prepare('Any X WHERE NOT Y evaluee X')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, True)
-        
+
     def test_not_relation_4_3(self):
         rqlst = self._prepare('Any Y WHERE NOT Y evaluee X')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, True)
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, False)
-        
+
     def test_not_relation_4_4(self):
         rqlst = self._prepare('Any X WHERE NOT Y evaluee X, Y is CWUser')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
@@ -184,7 +185,7 @@
         rqlst = self._prepare('Any X WHERE NOT Y evaluee X, Y eid %s, X is Note' % self.ueid)
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
         self.assertEquals(rqlst.solutions, [{'X': 'Note'}])
-        
+
     def test_not_relation_5_1(self):
         rqlst = self._prepare('Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), NOT X read_permission Y')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
@@ -201,64 +202,64 @@
         self.assertEquals(rqlst.defined_vars['A']._q_invariant, True)
 
     def test_not_relation_7(self):
-        rqlst = self._prepare('Any K,V WHERE P is CWProperty, P pkey K, P value V, NOT P for_user U') 
+        rqlst = self._prepare('Any K,V WHERE P is CWProperty, P pkey K, P value V, NOT P for_user U')
         self.assertEquals(rqlst.defined_vars['P']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['U']._q_invariant, True)
-       
-    def test_exists_1(self):        
+
+    def test_exists_1(self):
         rqlst = self._prepare('Any U WHERE U eid IN (1,2), EXISTS(X owned_by U)')
         self.assertEquals(rqlst.defined_vars['U']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, True)
-       
-    def test_exists_2(self):        
+
+    def test_exists_2(self):
         rqlst = self._prepare('Any U WHERE EXISTS(U eid IN (1,2), X owned_by U)')
         self.assertEquals(rqlst.defined_vars['U']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, True)
 
-    def test_exists_3(self):        
+    def test_exists_3(self):
         rqlst = self._prepare('Any U WHERE EXISTS(X owned_by U, X bookmarked_by U)')
         self.assertEquals(rqlst.defined_vars['U']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, True)
-        
+
     def test_exists_4(self):
         rqlst = self._prepare('Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), EXISTS(X read_permission Y)')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, False)
-        
+
     def test_exists_5(self):
         rqlst = self._prepare('DISTINCT Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), EXISTS(X read_permission Y)')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, True)
 
-    def test_not_exists_1(self):        
+    def test_not_exists_1(self):
         rqlst = self._prepare('Any U WHERE NOT EXISTS(X owned_by U, X bookmarked_by U)')
         self.assertEquals(rqlst.defined_vars['U']._q_invariant, False)
-        self.assertEquals(rqlst.defined_vars['X']._q_invariant, True)        
+        self.assertEquals(rqlst.defined_vars['X']._q_invariant, True)
 
-    def test_not_exists_2(self):        
+    def test_not_exists_2(self):
         rqlst = self._prepare('Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), NOT EXISTS(X read_permission Y)')
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, False)
 
-    def test_not_exists_distinct_1(self):        
+    def test_not_exists_distinct_1(self):
         rqlst = self._prepare('DISTINCT Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), NOT EXISTS(X read_permission Y)')
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, False)
-        
-    def test_or_1(self):        
+
+    def test_or_1(self):
         rqlst = self._prepare('Any X WHERE X concerne B OR C concerne X, B eid 12, C eid 13')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
 
-    def test_or_2(self):        
+    def test_or_2(self):
         rqlst = self._prepare('Any X WHERE X created_by U, X concerne B OR C concerne X, B eid 12, C eid 13')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, True)
-        self.assertEquals(rqlst.defined_vars['U']._q_invariant, True) 
+        self.assertEquals(rqlst.defined_vars['U']._q_invariant, True)
         self.assertEquals(rqlst.defined_vars['X'].stinfo['principal'].r_type, 'created_by')
 
-    def test_or_3(self):        
+    def test_or_3(self):
         rqlst = self._prepare('Any N WHERE A evaluee N or EXISTS(N todo_by U)')
         self.assertEquals(rqlst.defined_vars['N']._q_invariant, False)
-        self.assertEquals(rqlst.defined_vars['A']._q_invariant, True) 
-        self.assertEquals(rqlst.defined_vars['U']._q_invariant, True) 
-        
+        self.assertEquals(rqlst.defined_vars['A']._q_invariant, True)
+        self.assertEquals(rqlst.defined_vars['U']._q_invariant, True)
+
     def test_or_exists_1(self):
         # query generated by security rewriting
         rqlst = self._prepare('DISTINCT Any A,S WHERE A is Affaire, S nom "chouette", S is IN(Division, Societe, SubDivision),'
@@ -270,13 +271,13 @@
         self.assertEquals(rqlst.defined_vars['A']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['S']._q_invariant, False)
 
-    def test_or_exists_2(self):        
+    def test_or_exists_2(self):
         rqlst = self._prepare('Any U WHERE EXISTS(U in_group G, G name "managers") OR EXISTS(X owned_by U, X bookmarked_by U)')
         self.assertEquals(rqlst.defined_vars['U']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['G']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, True)
-        
-    def test_or_exists_3(self):        
+
+    def test_or_exists_3(self):
         rqlst = self._prepare('Any COUNT(S),CS GROUPBY CS ORDERBY 1 DESC LIMIT 10 '
                               'WHERE C is Societe, S concerne C, C nom CS, '
                               '(EXISTS(S owned_by 1)) OR (EXISTS(S documented_by N, N title "published"))')
@@ -286,7 +287,7 @@
                               '(EXISTS(S owned_by 1)) OR (EXISTS(S documented_by N, N title "published"))')
         self.assertEquals(rqlst.defined_vars['S']._q_invariant, True)
 
-    def test_nonregr_ambiguity(self):        
+    def test_nonregr_ambiguity(self):
         rqlst = self._prepare('Note N WHERE N attachment F')
         # N may be an image as well, not invariant
         self.assertEquals(rqlst.defined_vars['N']._q_invariant, False)
--- a/server/test/unittest_rqlrewrite.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/unittest_rqlrewrite.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from logilab.common.testlib import unittest_main, TestCase
 from logilab.common.testlib import mock_object
 
--- a/server/test/unittest_schemaserial.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/unittest_schemaserial.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,5 @@
 """unit tests for schema rql (de)serialization
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 import sys
--- a/server/test/unittest_security.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/unittest_security.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,5 @@
 """functional tests for server'security
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 import sys
 
@@ -15,15 +16,15 @@
         self.create_user('iaminusersgrouponly')
         self.readoriggroups = self.schema['Personne'].get_groups('read')
         self.addoriggroups = self.schema['Personne'].get_groups('add')
-        
+
     def tearDown(self):
         RepositoryBasedTC.tearDown(self)
         self.schema['Personne'].set_groups('read', self.readoriggroups)
         self.schema['Personne'].set_groups('add', self.addoriggroups)
 
-        
+
 class LowLevelSecurityFunctionTC(BaseSecurityTC):
-    
+
     def test_check_read_access(self):
         rql = u'Personne U where U nom "managers"'
         rqlst = self.repo.querier._rqlhelper.parse(rql).children[0]
@@ -38,7 +39,7 @@
                           check_read_access,
                           self.schema, cnx.user(self.current_session()), rqlst, solution)
         self.assertRaises(Unauthorized, cu.execute, rql)
-            
+
     def test_upassword_not_selectable(self):
         self.assertRaises(Unauthorized,
                           self.execute, 'Any X,P WHERE X is CWUser, X upassword P')
@@ -47,10 +48,10 @@
         cu = cnx.cursor()
         self.assertRaises(Unauthorized,
                           cu.execute, 'Any X,P WHERE X is CWUser, X upassword P')
-        
-    
+
+
 class SecurityTC(BaseSecurityTC):
-    
+
     def setUp(self):
         BaseSecurityTC.setUp(self)
         # implicitly test manager can add some entities
@@ -66,7 +67,7 @@
         cu.execute("INSERT Personne X: X nom 'bidule'")
         self.assertRaises(Unauthorized, cnx.commit)
         self.assertEquals(cu.execute('Personne X').rowcount, 1)
-        
+
     def test_insert_rql_permission(self):
         # test user can only add une affaire related to a societe he owns
         cnx = self.login('iaminusersgrouponly')
@@ -82,7 +83,7 @@
         cu.execute("INSERT Societe X: X nom 'chouette'")
         cu.execute("SET A concerne S WHERE A sujet 'cool', S nom 'chouette'")
         cnx.commit()
-        
+
     def test_update_security_1(self):
         cnx = self.login('anon')
         cu = cnx.cursor()
@@ -91,7 +92,7 @@
         self.assertRaises(Unauthorized, cnx.commit)
         self.restore_connection()
         self.assertEquals(self.execute('Personne X WHERE X nom "bidulechouette"').rowcount, 0)
-        
+
     def test_update_security_2(self):
         cnx = self.login('anon')
         cu = cnx.cursor()
@@ -109,7 +110,7 @@
         cu.execute("INSERT Personne X: X nom 'biduuule'")
         cu.execute("INSERT Societe X: X nom 'looogilab'")
         cu.execute("SET X travaille S WHERE X nom 'biduuule', S nom 'looogilab'")
-        
+
     def test_update_rql_permission(self):
         self.execute("SET A concerne S WHERE A is Affaire, S is Societe")
         self.commit()
@@ -121,13 +122,13 @@
         cnx.commit()
         # to actually get Unauthorized exception, try to update an entity we can read
         cu.execute("SET X nom 'toto' WHERE X is Societe")
-        self.assertRaises(Unauthorized, cnx.commit)        
+        self.assertRaises(Unauthorized, cnx.commit)
         cu.execute("INSERT Affaire X: X sujet 'pascool'")
         cu.execute("INSERT Societe X: X nom 'chouette'")
         cu.execute("SET A concerne S WHERE A sujet 'pascool', S nom 'chouette'")
         cu.execute("SET X sujet 'habahsicestcool' WHERE X sujet 'pascool'")
         cnx.commit()
-    
+
     def test_delete_security(self):
         # FIXME: sample below fails because we don't detect "owner" can't delete
         # user anyway, and since no user with login == 'bidule' exists, no
@@ -139,14 +140,14 @@
         cnx = self.login('iaminusersgrouponly')
         cu = cnx.cursor()
         self.assertRaises(Unauthorized, cu.execute, "DELETE CWGroup Y WHERE Y name 'staff'")
-        
+
     def test_delete_rql_permission(self):
         self.execute("SET A concerne S WHERE A is Affaire, S is Societe")
         self.commit()
         # test user can only dele une affaire related to a societe he owns
         cnx = self.login('iaminusersgrouponly')
         cu = cnx.cursor()
-        # this won't actually do anything since the selection query won't return anything        
+        # this won't actually do anything since the selection query won't return anything
         cu.execute("DELETE Affaire X")
         cnx.commit()
         # to actually get Unauthorized exception, try to delete an entity we can read
@@ -227,7 +228,7 @@
         self.assertRaises(Unauthorized, cnx.commit)
 
     # read security test
-    
+
     def test_read_base(self):
         self.schema['Personne'].set_groups('read', ('users', 'managers'))
         cnx = self.login('anon')
@@ -256,7 +257,7 @@
         self.assertEquals(rset.rows, [[aff2]])
         rset = cu.execute('Affaire X WHERE NOT X eid %(x)s', {'x': aff2}, 'x')
         self.assertEquals(rset.rows, [])
-        
+
     def test_read_erqlexpr_has_text1(self):
         aff1 = self.execute("INSERT Affaire X: X sujet 'cool'")[0][0]
         card1 = self.execute("INSERT Card X: X title 'cool'")[0][0]
@@ -286,7 +287,7 @@
         rset = cu.execute('Any N WHERE N has_text "bidule"')
         self.assertEquals(len(rset.rows), 1, rset.rows)
         rset = cu.execute('Any N WITH N BEING (Any N WHERE N has_text "bidule")')
-        self.assertEquals(len(rset.rows), 1, rset.rows)        
+        self.assertEquals(len(rset.rows), 1, rset.rows)
 
     def test_read_erqlexpr_optional_rel(self):
         self.execute("INSERT Personne X: X nom 'bidule'")
@@ -304,7 +305,7 @@
         cnx = self.login('iaminusersgrouponly')
         cu = cnx.cursor()
         rset = cu.execute('Any COUNT(X) WHERE X is Affaire')
-        self.assertEquals(rset.rows, [[0]])        
+        self.assertEquals(rset.rows, [[0]])
         aff2 = cu.execute("INSERT Affaire X: X sujet 'cool'")[0][0]
         soc1 = cu.execute("INSERT Societe X: X nom 'chouette'")[0][0]
         cu.execute("SET A concerne S WHERE A is Affaire, S is Societe")
@@ -320,7 +321,7 @@
         values = dict(rset)
         self.assertEquals(values['Affaire'], 1)
         self.assertEquals(values['Societe'], 2)
-        
+
 
     def test_attribute_security(self):
         # only managers should be able to edit the 'test' attribute of Personne entities
@@ -343,7 +344,7 @@
         cu.execute('SET X web "http://www.logilab.org" WHERE X eid %(x)s', {'x': eid}, 'x')
         cnx.commit()
         cnx.close()
-        
+
     def test_attribute_security_rqlexpr(self):
         # Note.para attribute editable by managers or if the note is in "todo" state
         eid = self.execute("INSERT Note X: X para 'bidule', X in_state S WHERE S name 'done'")[0][0]
@@ -384,10 +385,10 @@
         self.failUnless(x.creation_date)
         cnx.rollback()
 
-        
+
 class BaseSchemaSecurityTC(BaseSecurityTC):
     """tests related to the base schema permission configuration"""
-        
+
     def test_user_can_delete_object_he_created(self):
         # even if some other user have changed object'state
         cnx = self.login('iaminusersgrouponly')
@@ -400,7 +401,7 @@
         self.execute('SET X in_state S WHERE X ref "ARCT01", S name "ben non"')
         self.commit()
         self.assertEquals(len(self.execute('TrInfo X WHERE X wf_info_for A, A ref "ARCT01"')),
-                          2) 
+                          2)
         self.assertEquals(len(self.execute('TrInfo X WHERE X wf_info_for A, A ref "ARCT01",'
                                            'X owned_by U, U login "admin"')),
                           1) # TrInfo at the above state change
@@ -426,19 +427,19 @@
         self.assert_(cu.execute('CWGroup X'))
         # should only be able to read the anonymous user, not another one
         origuser = self.session.user
-        self.assertRaises(Unauthorized, 
+        self.assertRaises(Unauthorized,
                           cu.execute, 'CWUser X WHERE X eid %(x)s', {'x': origuser.eid}, 'x')
         # nothing selected, nothing updated, no exception raised
         #self.assertRaises(Unauthorized,
         #                  cu.execute, 'SET X login "toto" WHERE X eid %(x)s',
         #                  {'x': self.user.eid})
-        
+
         rset = cu.execute('CWUser X WHERE X eid %(x)s', {'x': anon.eid}, 'x')
         self.assertEquals(rset.rows, [[anon.eid]])
         # but can't modify it
         cu.execute('SET X login "toto" WHERE X eid %(x)s', {'x': anon.eid})
         self.assertRaises(Unauthorized, cnx.commit)
-    
+
     def test_in_group_relation(self):
         cnx = self.login('iaminusersgrouponly')
         cu = cnx.cursor()
@@ -454,7 +455,7 @@
         cu = cnx.cursor()
         rql = u"SET X owned_by U WHERE U login 'iaminusersgrouponly', X is Personne"
         self.assertRaises(Unauthorized, cu.execute, rql)
-        
+
     def test_bookmarked_by_guests_security(self):
         beid1 = self.execute('INSERT Bookmark B: B path "?vid=manage", B title "manage"')[0][0]
         beid2 = self.execute('INSERT Bookmark B: B path "?vid=index", B title "index", B bookmarked_by U WHERE U login "anon"')[0][0]
@@ -475,7 +476,7 @@
         self.assertRaises(Unauthorized,
                           cu.execute, 'SET B bookmarked_by U WHERE U eid %(x)s, B eid %(b)s',
                           {'x': anoneid, 'b': beid1}, 'x')
-        
+
 
     def test_ambigous_ordered(self):
         cnx = self.login('anon')
@@ -494,7 +495,7 @@
         # needed to avoid check_perm error
         session.set_pool()
         # needed to remove rql expr granting update perm to the user
-        self.schema['Affaire'].set_rqlexprs('update', ()) 
+        self.schema['Affaire'].set_rqlexprs('update', ())
         self.assertRaises(Unauthorized,
                           self.schema['Affaire'].check_perm, session, 'update', eid)
         cu = cnx.cursor()
@@ -506,6 +507,6 @@
         # the best would probably ValidationError if the transition doesn't exist
         # from the current state but Unauthorized if it exists but user can't pass it
         self.assertRaises(ValidationError, cu.execute, rql, {'x': cnx.user(self.current_session()).eid}, 'x')
-        
+
 if __name__ == '__main__':
     unittest_main()
--- a/server/test/unittest_session.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/unittest_session.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from logilab.common.testlib import TestCase, unittest_main, mock_object
 
 from cubicweb.server.session import _make_description
@@ -6,12 +13,12 @@
     def __init__(self, name):
         self.name = name
         self.children = []
-        
+
     def get_type(self, solution, args=None):
         return solution[self.name]
     def as_string(self):
         return self.name
-    
+
 class Function:
     def __init__(self, name, varname):
         self.name = name
--- a/server/test/unittest_sqlutils.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/unittest_sqlutils.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,5 @@
 """unit tests for module cubicweb.server.sqlutils
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 import sys
@@ -20,12 +21,12 @@
     def test_init(self):
         o = SQLAdapterMixIn(BASE_CONFIG)
         self.assertEquals(o.encoding, 'UTF-8')
-        
+
     def test_init_encoding(self):
         config = BASE_CONFIG.copy()
         config['db-encoding'] = 'ISO-8859-1'
         o = SQLAdapterMixIn(config)
         self.assertEquals(o.encoding, 'ISO-8859-1')
-        
+
 if __name__ == '__main__':
     unittest_main()
--- a/server/test/unittest_ssplanner.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/unittest_ssplanner.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from cubicweb.devtools import init_test_database
 from cubicweb.devtools.repotest import BasePlannerTC, test_plan
 from cubicweb.server.ssplanner import SSPlanner
@@ -8,7 +15,7 @@
 class SSPlannerTC(BasePlannerTC):
     repo = repo
     _test = test_plan
-    
+
     def setUp(self):
         BasePlannerTC.setUp(self)
         self.planner = SSPlanner(self.o.schema, self.o._rqlhelper)
@@ -33,9 +40,9 @@
                                         {'X': 'State', 'XN': 'String'},
                                         {'X': 'Tag', u'XN': 'String'},
                                         {'X': 'Transition', 'XN': 'String'}])],
-                     None, None, 
+                     None, None,
                      [self.system], None, [])])
-    
+
     def test_groupeded_ambigous_sol(self):
         self._test('Any XN,COUNT(X) GROUPBY XN WHERE X name XN',
                    [('OneFetchStep', [('Any XN,COUNT(X) GROUPBY XN WHERE X name XN',
@@ -52,9 +59,9 @@
                                         {'X': 'State', 'XN': 'String'},
                                         {'X': 'Tag', u'XN': 'String'},
                                         {'X': 'Transition', 'XN': 'String'}])],
-                     None, None, 
+                     None, None,
                      [self.system], None, [])])
-        
+
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main
     unittest_main()
--- a/server/test/unittest_tools.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/test/unittest_tools.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from logilab.common.testlib import TestCase, unittest_main
 
 class ImportTC(TestCase):
@@ -6,6 +13,6 @@
         import cubicweb.server.server
         import cubicweb.server.checkintegrity
         import cubicweb.server.serverctl
-        
+
 if __name__ == '__main__':
     unittest_main()
--- a/server/utils.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/server/utils.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Some utilities for the CubicWeb server.
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -46,7 +47,7 @@
             for item in seqin[0]:
                 newcomb = comb + [item]     # add next item to current combination
                 # call rloop w/ remaining seqs, newcomb
-                for item in rloop(seqin[1:], newcomb):   
+                for item in rloop(seqin[1:], newcomb):
                     yield item          # seqs and newcomb
         else:                           # processing last sequence
             yield comb                  # comb finished, add to list
@@ -63,23 +64,21 @@
 DEFAULT_MSG = 'we need a manager connection on the repository \
 (the server doesn\'t have to run, even should better not)'
 
-def manager_userpasswd(user=None, passwd=None, msg=DEFAULT_MSG, confirm=False):
+def manager_userpasswd(user=None, msg=DEFAULT_MSG, confirm=False,
+                       passwdmsg='password'):
     if not user:
         print msg
         while not user:
             user = raw_input('login: ')
-        passwd = getpass('password: ')
-        if confirm:
-            while True:
-                passwd2 = getpass('confirm password: ')
-                if passwd == passwd2:
-                    break
-                print 'password doesn\'t match'
-                passwd = getpass('password: ')
         user = unicode(user, sys.stdin.encoding)
-    elif not passwd:
-        assert not confirm
-        passwd = getpass('password for %s: ' % user)
+    passwd = getpass('%s: ' % passwdmsg)
+    if confirm:
+        while True:
+            passwd2 = getpass('confirm password: ')
+            if passwd == passwd2:
+                break
+            print 'password doesn\'t match'
+            passwd = getpass('password: ')
     # XXX decode password using stdin encoding then encode it using appl'encoding
     return user, passwd
 
@@ -95,7 +94,7 @@
                 self.start()
         self.func = auto_restart_func
         self.name = func.__name__
-        
+
     def start(self):
         self._t = Timer(self.interval, self.func)
         self._t.start()
@@ -120,7 +119,7 @@
         Thread.__init__(self, target=auto_remove_func)
         self.running_threads = running_threads
         self._name = target.__name__
-        
+
     def start(self):
         self.running_threads.append(self)
         Thread.start(self)
--- a/setup.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/setup.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,7 +1,14 @@
 #!/usr/bin/env python
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 # pylint: disable-msg=W0142,W0403,W0404,W0613,W0622,W0622,W0704,R0904,C0103,E0611
 #
-# Copyright (c) 2003 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2003-2009 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This program is free software; you can redistribute it and/or modify it under
@@ -56,7 +63,7 @@
 
 BASE_BLACKLIST = ('CVS', 'debian', 'dist', 'build', '__buildlog')
 IGNORED_EXTENSIONS = ('.pyc', '.pyo', '.elc')
-    
+
 
 def ensure_scripts(linux_scripts):
     """
@@ -152,7 +159,7 @@
             for directory in include_dirs:
                 dest = join(self.install_dir, base, directory)
                 export(directory, dest)
-        
+
 def install(**kwargs):
     """setup entry point"""
     if subpackage_of:
@@ -177,6 +184,6 @@
                  cmdclass={'install_lib': MyInstallLib},
                  **kwargs
                  )
-            
+
 if __name__ == '__main__' :
     install()
--- a/skeleton/__pkginfo__.py.tmpl	Wed Jun 03 09:09:33 2009 +0200
+++ b/skeleton/__pkginfo__.py.tmpl	Wed Jun 03 09:15:20 2009 +0200
@@ -32,7 +32,7 @@
     return [join(dirpath, fname) for fname in _listdir(dirpath)
             if fname[0] != '.' and not fname.endswith('.pyc')
             and not fname.endswith('~')
-            and not isdir(join(dirpath, fname))]¶
+            and not isdir(join(dirpath, fname))]
 
 from glob import glob
 try:
--- a/skeleton/entities.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/skeleton/entities.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,7 @@
-"""this contains the cube-specific entities' classes"""
+"""this contains the cube-specific entities' classes
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
--- a/skeleton/migration/postcreate.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/skeleton/migration/postcreate.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,2 +1,9 @@
 # postcreate script. You could setup a workflow here for example
+"""
 
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
+
--- a/skeleton/migration/precreate.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/skeleton/migration/precreate.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
 # Instructions here will be read before reading the schema
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 # You could create your own groups here, like in :
 #   add_entity('CWGroup', name=u'mygroup')
--- a/skeleton/schema.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/skeleton/schema.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,8 @@
 # cube's specific schema
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
--- a/skeleton/setup.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/skeleton/setup.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,6 +1,13 @@
 #!/usr/bin/env python
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 # pylint: disable-msg=W0404,W0622,W0704,W0613,W0152
-# Copyright (c) 2003-2004 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2003-2009 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This program is free software; you can redistribute it and/or modify it under
@@ -45,6 +52,6 @@
                  url=web,
                  data_files=data_files,
                  **kwargs)
-            
+
 if __name__ == '__main__' :
     install()
--- a/skeleton/test/pytestconf.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/skeleton/test/pytestconf.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 import os
 import pwd
 
--- a/skeleton/test/realdb_test_CUBENAME.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/skeleton/test/realdb_test_CUBENAME.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from cubicweb.devtools import buildconfig, loadconfig
 from cubicweb.devtools.testlib import RealDBTest
 
@@ -18,7 +25,7 @@
     def test_all_primaries(self):
         for rset in self.iter_individual_rsets(limit=50):
             yield self.view, 'primary', rset, rset.req.reset_headers()
-    
+
     ## startup views
     def test_startup_views(self):
         for vid in self.list_startup_views():
--- a/skeleton/test/test_CUBENAME.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/skeleton/test/test_CUBENAME.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""template automatic tests"""
+"""template automatic tests
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 from logilab.common.testlib import TestCase, unittest_main
 
--- a/skeleton/views.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/skeleton/views.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,7 @@
-"""cube-specific forms/views/actions/components"""
+"""cube-specific forms/views/actions/components
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
--- a/sobjects/__init__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/sobjects/__init__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,7 @@
-"""server side objects"""
+"""server side objects
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
--- a/sobjects/email.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/sobjects/email.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """hooks to ensure use_email / primary_email relations consistency
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -15,50 +16,50 @@
     """
     rtype = 'use_email'
     fromeid = toeid = None # make pylint happy
-    
+
     def condition(self):
         """check entity has use_email set for the email address"""
         return not self.session.unsafe_execute(
             'Any X WHERE X eid %(x)s, X use_email Y, Y eid %(y)s',
             {'x': self.fromeid, 'y': self.toeid}, 'x')
-    
+
     def precommit_event(self):
         session = self.session
         if self.condition():
             session.unsafe_execute(
                 'SET X %s Y WHERE X eid %%(x)s, Y eid %%(y)s' % self.rtype,
                 {'x': self.fromeid, 'y': self.toeid}, 'x')
-    
+
 class SetPrimaryEmailRelationOp(SetUseEmailRelationOp):
     rtype = 'primary_email'
-    
+
     def condition(self):
         """check entity has no primary_email set"""
         return not self.session.unsafe_execute(
             'Any X WHERE X eid %(x)s, X primary_email Y',
             {'x': self.fromeid}, 'x')
 
-    
+
 class SetPrimaryEmailHook(Hook):
     """notify when a bug or story or version has its state modified"""
     events = ('after_add_relation',)
     accepts = ('use_email',)
-    
+
     def call(self, session, fromeid, rtype, toeid):
         subjtype = session.describe(fromeid)[0]
         eschema = self.vreg.schema[subjtype]
         if 'primary_email' in eschema.subject_relations():
-            SetPrimaryEmailRelationOp(session, vreg=self.vreg, 
+            SetPrimaryEmailRelationOp(session, vreg=self.vreg,
                                       fromeid=fromeid, toeid=toeid)
 
 class SetUseEmailHook(Hook):
     """notify when a bug or story or version has its state modified"""
     events = ('after_add_relation',)
     accepts = ('primary_email',)
-    
+
     def call(self, session, fromeid, rtype, toeid):
         subjtype = session.describe(fromeid)[0]
         eschema = self.vreg.schema[subjtype]
         if 'use_email' in eschema.subject_relations():
-            SetUseEmailRelationOp(session, vreg=self.vreg, 
+            SetUseEmailRelationOp(session, vreg=self.vreg,
                                   fromeid=fromeid, toeid=toeid)
--- a/sobjects/hooks.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/sobjects/hooks.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """various library content hooks
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -15,7 +16,7 @@
     """ensure user logins are stripped"""
     events = ('before_add_entity', 'before_update_entity',)
     accepts = ('CWUser',)
-    
+
     def call(self, session, entity):
         if 'login' in entity and entity['login']:
             entity['login'] = entity['login'].strip()
@@ -30,12 +31,12 @@
                                           {'x': self.beid}, 'x'):
                 session.unsafe_execute('DELETE Bookmark X WHERE X eid %(x)s',
                                        {'x': self.beid}, 'x')
-        
+
 class DelBookmarkedByHook(Hook):
     """ensure user logins are stripped"""
     events = ('after_delete_relation',)
     accepts = ('bookmarked_by',)
-    
+
     def call(self, session, subj, rtype, obj):
         AutoDeleteBookmark(session, beid=subj)
 
@@ -60,4 +61,3 @@
                         fmt = entity.get_value(metaattr)
                     if fmt == 'text/html':
                         entity[attr] = soup2xhtml(value, session.encoding)
-
--- a/sobjects/notification.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/sobjects/notification.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """some hooks and views to handle notification on entity's changes
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/sobjects/supervising.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/sobjects/supervising.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/sobjects/test/data/schema.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/sobjects/test/data/schema.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 class comments(RelationDefinition):
     subject = 'Comment'
     object = 'Card'
--- a/sobjects/test/data/sobjects/__init__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/sobjects/test/data/sobjects/__init__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,6 +1,12 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
+from cubicweb.selectors import implements
 from cubicweb.sobjects.notification import StatusChangeMixIn, NotificationView
 
 class UserStatusChangeView(StatusChangeMixIn, NotificationView):
-    accepts = ('CWUser',)
-    
-    
+    __select__ = NotificationView.__select__ & implements('CWUser')
--- a/sobjects/test/unittest_email.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/sobjects/test/unittest_email.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from cubicweb.devtools.apptest import EnvBasedTC
 
 class EmailAddressHooksTC(EnvBasedTC):
@@ -22,7 +29,7 @@
         self.commit()
         self.assertEquals(self.execute('Any A WHERE U use_email X, U login "admin", X address A')[0][0],
                           'admin@logilab.fr')
-        
+
 
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main
--- a/sobjects/test/unittest_hooks.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/sobjects/test/unittest_hooks.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from logilab.common.testlib import unittest_main
 from cubicweb.devtools.apptest import EnvBasedTC
 
@@ -13,7 +20,7 @@
                              {'e': u.eid})[0][0]
         self.assertEquals(tname, 'jijoe')
 
-    
+
     def test_auto_delete_bookmarks(self):
         beid = self.execute('INSERT Bookmark X: X title "hop", X path "view", X bookmarked_by U '
                             'WHERE U login "admin"')[0][0]
@@ -25,6 +32,6 @@
         self.execute('DELETE X bookmarked_by U WHERE U login "anon"')
         self.commit()
         self.failIf(self.execute('Any X WHERE X eid %(x)s', {'x': beid}, 'x'))
-        
+
 if __name__ == '__main__':
     unittest_main()
--- a/sobjects/test/unittest_notification.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/sobjects/test/unittest_notification.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,11 @@
 # -*- coding: iso-8859-1 -*-
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from socket import gethostname
 
 from logilab.common.testlib import unittest_main, TestCase
@@ -78,9 +85,9 @@
 
 yeah
 
-url: http://testing.fr/cubicweb/euser/toto
+url: http://testing.fr/cubicweb/cwuser/toto
 ''')
-        self.assertEquals(v.subject(), 'status changed euser #%s (admin)' % u.eid)
+        self.assertEquals(v.subject(), 'status changed cwuser #%s (admin)' % u.eid)
 
 if __name__ == '__main__':
     unittest_main()
--- a/sobjects/test/unittest_supervising.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/sobjects/test/unittest_supervising.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,11 @@
 # -*- coding: iso-8859-1 -*-
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 import re
 
 from logilab.common.testlib import unittest_main
@@ -43,23 +50,23 @@
         data = re.sub('/\d+', '/EID', data)
         self.assertTextEquals('''user admin has made the following change(s):
 
-* added euser #EID (toto)
-  http://testing.fr/cubicweb/euser/toto
+* added cwuser #EID (toto)
+  http://testing.fr/cubicweb/cwuser/toto
 
-* added relation in_group from euser #EID to egroup #EID
+* added relation in_group from cwuser #EID to cwgroup #EID
 
 * deleted card #EID (une news !)
 
-* added relation bookmarked_by from bookmark #EID to euser #EID
+* added relation bookmarked_by from bookmark #EID to cwuser #EID
 
 * updated comment #EID (#EID)
   http://testing.fr/cubicweb/comment/EID
 
 * deleted relation comments from comment #EID to card #EID
 
-* changed state of euser #EID (anon)
+* changed state of cwuser #EID (anon)
   from state activated to state deactivated
-  http://testing.fr/cubicweb/euser/anon''',
+  http://testing.fr/cubicweb/cwuser/anon''',
                               data)
         # check prepared email
         op._prepare_email()
--- a/test/data/cubes/file/__pkginfo__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/test/data/cubes/file/__pkginfo__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,5 +1,11 @@
 # pylint: disable-msg=W0622
-"""cubicweb-file packaging information"""
+"""cubicweb-file packaging information
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 distname = "cubicweb-file"
 modname = distname.split('-', 1)[1]
--- a/test/data/cubes/mycube/__init__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/test/data/cubes/mycube/__init__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,7 @@
-"""mycube's __init__"""
+"""mycube's __init__
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
--- a/test/data/cubes/mycube/__pkginfo__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/test/data/cubes/mycube/__pkginfo__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,1 +1,8 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 distname = 'cubicweb-mycube'
--- a/test/data/entities.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/test/data/entities.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from cubicweb.entities import AnyEntity, fetch_config
 
 class Societe(AnyEntity):
--- a/test/data/erqlexpr_on_ertype.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/test/data/erqlexpr_on_ertype.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 class ToTo(EntityType):
     permissions = {
         'read': ('managers',),
@@ -6,7 +13,7 @@
         'delete': ('managers',),
         }
     toto = SubjectRelation('TuTu')
-    
+
 class TuTu(EntityType):
     permissions = {
         'read': ('managers',),
--- a/test/data/rqlexpr_on_ertype_read.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/test/data/rqlexpr_on_ertype_read.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 class ToTo(EntityType):
     permissions = {
         'read': ('managers',),
@@ -6,7 +13,7 @@
         'delete': ('managers',),
         }
     toto = SubjectRelation('TuTu')
-    
+
 class TuTu(EntityType):
     permissions = {
         'read': ('managers',),
--- a/test/data/rrqlexpr_on_attr.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/test/data/rrqlexpr_on_attr.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 class ToTo(EntityType):
     permissions = {
         'read': ('managers',),
@@ -6,7 +13,7 @@
         'delete': ('managers',),
         }
     attr = String()
-    
+
 class attr(RelationType):
     permissions = {
         'read': ('managers', ),
--- a/test/data/rrqlexpr_on_eetype.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/test/data/rrqlexpr_on_eetype.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 class ToTo(EntityType):
     permissions = {
         'read': ('managers', RRQLExpression('S bla Y'),),
--- a/test/data/schema.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/test/data/schema.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 class Personne(EntityType):
     nom = String(required=True)
     prenom = String()
--- a/test/unittest_cwconfig.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/test/unittest_cwconfig.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 import sys
 import os
 from os.path import dirname, join, abspath
--- a/test/unittest_cwctl.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/test/unittest_cwctl.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 import sys
 import os
 from cStringIO import StringIO
@@ -19,10 +26,10 @@
         sys.stdout = self.stream
     def tearDown(self):
         sys.stdout = sys.__stdout__
-        
+
     def test_list(self):
         from cubicweb.cwctl import ListCommand
         ListCommand().run([])
-        
+
 if __name__ == '__main__':
     unittest_main()
--- a/test/unittest_dbapi.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/test/unittest_dbapi.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from cubicweb import ConnectionError
 from cubicweb.dbapi import ProgrammingError
 from cubicweb.devtools.apptest import EnvBasedTC
--- a/test/unittest_entity.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/test/unittest_entity.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,5 +1,11 @@
 # -*- coding: utf-8 -*-
-"""unit tests for cubicweb.web.views.entities module"""
+"""unit tests for cubicweb.web.views.entities module
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 from datetime import datetime
 
@@ -374,14 +380,15 @@
         self.assertEquals(person.prenom, u'sylvain')
         self.assertEquals(person.nom, u'thénault')
 
-    def test_metainformation(self):
+    def test_metainformation_and_external_absolute_url(self):
         note = self.add_entity('Note', type=u'z')
         metainf = note.metainformation()
         self.assertEquals(metainf, {'source': {'adapter': 'native', 'uri': 'system'}, 'type': u'Note', 'extid': None})
         self.assertEquals(note.absolute_url(), 'http://testing.fr/cubicweb/note/%s' % note.eid)
         metainf['source'] = metainf['source'].copy()
         metainf['source']['base-url']  = 'http://cubicweb2.com/'
-        self.assertEquals(note.absolute_url(), 'http://cubicweb2.com/note/%s' % note.eid)
+        metainf['extid']  = 1234
+        self.assertEquals(note.absolute_url(), 'http://cubicweb2.com/note/1234')
 
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main
--- a/test/unittest_rset.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/test/unittest_rset.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,5 +1,11 @@
 # coding: utf-8
-"""unit tests for module cubicweb.common.utils"""
+"""unit tests for module cubicweb.common.utils
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from __future__ import with_statement
 
 from logilab.common.testlib import TestCase, unittest_main
--- a/test/unittest_rtags.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/test/unittest_rtags.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from logilab.common.testlib import TestCase, unittest_main
 from cubicweb.rtags import RelationTags, RelationTagsSet
 
--- a/test/unittest_schema.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/test/unittest_schema.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""unit tests for module cubicweb.schema"""
+"""unit tests for module cubicweb.schema
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 import sys
 from os.path import join, isabs, basename, dirname
@@ -96,7 +102,7 @@
                          ['Any X WHERE X travaille S, S owned_by U, X eid %(x)s, U eid %(u)s'])
         eperson.set_groups('read', ('managers',))
         self.assertEqual(eperson.get_groups('read'), set(('managers',)))
-        
+
     def test_relation_perms(self):
         rconcerne = schema.rschema('concerne')
         rconcerne.set_default_groups()
@@ -112,19 +118,19 @@
         self.assertRaises(RQLSyntaxError, ERQLExpression, '1')
         expr = ERQLExpression('X travaille S, S owned_by U')
         self.assertEquals(str(expr), 'Any X WHERE X travaille S, S owned_by U, X eid %(x)s, U eid %(u)s')
-        
+
     def test_rrqlexpression(self):
         self.assertRaises(Exception, RRQLExpression, '1')
         self.assertRaises(RQLSyntaxError, RRQLExpression, 'O X Y')
         expr = RRQLExpression('U has_update_permission O')
         self.assertEquals(str(expr), 'Any O WHERE U has_update_permission O, O eid %(o)s, U eid %(u)s')
-        
+
 
 loader = CubicWebSchemaLoader()
 config = TestConfiguration('data')
 config.bootstrap_cubes()
 loader.lib_directory = config.schemas_lib_dir()
-    
+
 class SQLSchemaReaderClassTest(TestCase):
 
     def test_knownValues_include_schema_files(self):
@@ -139,15 +145,15 @@
         self.assertEquals(schema.name, 'data')
         entities = [str(e) for e in schema.entities()]
         entities.sort()
-        expected_entities = ['Bookmark', 'Boolean', 'Bytes', 'Card', 
+        expected_entities = ['Bookmark', 'Boolean', 'Bytes', 'Card',
                              'Date', 'Datetime', 'Decimal',
                              'CWCache', 'CWConstraint', 'CWConstraintType', 'CWEType',
                              'CWAttribute', 'CWGroup', 'EmailAddress', 'CWRelation',
                              'CWPermission', 'CWProperty', 'CWRType', 'CWUser',
                              'File', 'Float', 'Image', 'Int', 'Interval', 'Note',
                              'Password', 'Personne',
-                             'RQLExpression', 
-                             'Societe', 'State', 'String', 'SubNote', 'Tag', 'Time', 
+                             'RQLExpression',
+                             'Societe', 'State', 'String', 'SubNote', 'Tag', 'Time',
                              'Transition', 'TrInfo']
         self.assertListEquals(entities, sorted(expected_entities))
         relations = [str(r) for r in schema.relations()]
@@ -155,7 +161,7 @@
         expected_relations = ['add_permission', 'address', 'alias',
                               'allowed_transition', 'bookmarked_by', 'canonical',
 
-                              'cardinality', 'comment', 'comment_format', 
+                              'cardinality', 'comment', 'comment_format',
                               'composite', 'condition', 'connait', 'constrained_by', 'content',
                               'content_format', 'created_by', 'creation_date', 'cstrtype',
 
@@ -167,7 +173,7 @@
                               'final', 'firstname', 'for_user',
                               'from_entity', 'from_state', 'fulltext_container', 'fulltextindexed',
 
-                              'has_text', 
+                              'has_text',
                               'identical_to', 'identity', 'in_group', 'in_state', 'indexed',
                               'initial_state', 'inlined', 'internationalizable', 'is', 'is_instance_of',
 
@@ -179,20 +185,20 @@
 
                               'ordernum', 'owned_by',
 
-                              'path', 'pkey', 'prenom', 'primary_email', 
+                              'path', 'pkey', 'prenom', 'primary_email',
 
                               'read_permission', 'relation_type', 'require_group',
-                              
+
                               'specializes', 'state_of', 'surname', 'symetric', 'synopsis',
 
                               'tags', 'timestamp', 'title', 'to_entity', 'to_state', 'transition_of', 'travaille', 'type',
 
                               'upassword', 'update_permission', 'use_email',
 
-                              'value', 
+                              'value',
 
                               'wf_info_for', 'wikiid']
-    
+
         self.assertListEquals(relations, expected_relations)
 
         eschema = schema.eschema('CWUser')
@@ -234,16 +240,16 @@
         ex = self.assertRaises(BadSchemaDefinition,
                                self.loader._build_schema, 'toto', False)
         self.assertEquals(str(ex), msg)
-        
+
     def test_rrqlexpr_on_etype(self):
         self._test('rrqlexpr_on_eetype.py', "can't use RRQLExpression on an entity type, use an ERQLExpression (ToTo)")
-        
+
     def test_erqlexpr_on_rtype(self):
         self._test('erqlexpr_on_ertype.py', "can't use ERQLExpression on a relation type, use a RRQLExpression (toto)")
-        
+
     def test_rqlexpr_on_rtype_read(self):
         self._test('rqlexpr_on_ertype_read.py', "can't use rql expression for read permission of a relation type (toto)")
-        
+
     def test_rrqlexpr_on_attr(self):
         self._test('rrqlexpr_on_attr.py', "can't use RRQLExpression on a final relation type (eg attribute relation), use an ERQLExpression (attr)")
 
--- a/test/unittest_selectors.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/test/unittest_selectors.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """unit tests for selectors mechanism
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 from logilab.common.testlib import TestCase, unittest_main
--- a/test/unittest_utils.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/test/unittest_utils.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""unit tests for module cubicweb.common.utils"""
+"""unit tests for module cubicweb.common.utils
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 from logilab.common.testlib import TestCase, unittest_main
 
@@ -9,7 +15,7 @@
     def test_1(self):
         self.assertNotEquals(make_uid('xyz'), make_uid('abcd'))
         self.assertNotEquals(make_uid('xyz'), make_uid('xyz'))
-        
+
     def test_2(self):
         d = {}
         while len(d)<10000:
@@ -18,7 +24,7 @@
                 self.fail(len(d))
             d[uid] = 1
 
-        
+
 class UStringIOTC(TestCase):
     def test_boolean_value(self):
         self.assert_(UStringIO())
@@ -31,7 +37,7 @@
         for i in xrange(12):
             l.append(i)
         self.assertEquals(l, range(2, 12))
-    
+
     def test_extend(self):
         testdata = [(range(5), range(5)),
                     (range(10), range(10)),
@@ -42,6 +48,6 @@
             l.extend(extension)
             yield self.assertEquals, l, expected
 
-   
+
 if __name__ == '__main__':
     unittest_main()
--- a/test/unittest_vregistry.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/test/unittest_vregistry.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from logilab.common.testlib import unittest_main, TestCase
 
 from os.path import join
--- a/toolsutils.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/toolsutils.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """some utilities for cubicweb tools
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -39,7 +40,7 @@
         if ex.errno != errno.EEXIST:
             raise
         print 'directory %s already exists' % directory
-                
+
 def create_symlink(source, target):
     """create a symbolic link"""
     if exists(target):
@@ -51,7 +52,7 @@
     import shutil
     print '[copy] %s <-- %s' % (target, source)
     shutil.copy2(source, target)
-    
+
 def rm(whatever):
     import shutil
     shutil.rmtree(whatever)
@@ -66,7 +67,7 @@
     diffs = p_output.read()
     if diffs:
         if askconfirm:
-            print 
+            print
             print diffs
             action = raw_input('replace (N/y/q) ? ').lower()
         else:
@@ -120,7 +121,7 @@
                 show_diffs(tfpath, fpath, askconfirm)
             else:
                 shutil.copyfile(fpath, tfpath)
-                
+
 def fill_templated_file(fpath, tfpath, context):
     fobj = file(tfpath, 'w')
     templated = file(fpath).read()
@@ -158,7 +159,7 @@
     :param config_file: path to the configuration file
 
     :rtype: dict
-    :return: a dictionary with specified values associated to option names 
+    :return: a dictionary with specified values associated to option names
     """
     from logilab.common.fileutils import lines
     config = current = {}
@@ -194,10 +195,10 @@
 
     :type default: str
     :param default: default value if the environment variable is not defined
-    
+
     :type name: str
     :param name: the informal name of the path, used for error message
-    
+
     :rtype: str
     :return: the value of the environment variable or the default value
 
@@ -243,12 +244,12 @@
             msg = 'No helper for command %s using %s configuration' % (
                 cmdname, config.name)
             raise ConfigurationError(msg)
-        
+
     def fail(self, reason):
         print "command failed:", reason
         sys.exit(1)
-    
-                    
+
+
 def main_run(args, doc):
     """command line tool"""
     try:
@@ -289,4 +290,4 @@
     if not password:
         password = getpass('password: ')
     return connect(user=user, password=password, host=optconfig.host, database=appid)
-    
+
--- a/utils.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/utils.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,15 +1,16 @@
 """Some utilities for CubicWeb server/clients.
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
 import locale
 from md5 import md5
 from datetime import datetime, timedelta, date
-from time import time
+from time import time, mktime
 from random import randint, seed
 from calendar import monthrange
 
@@ -37,6 +38,9 @@
     assert isinstance(somedate, date), repr(somedate)
     return datetime(somedate.year, somedate.month, somedate.day)
 
+def datetime2ticks(date):
+    return mktime(date.timetuple()) * 1000
+
 ONEDAY = timedelta(days=1)
 ONEWEEK = timedelta(days=7)
 
@@ -99,7 +103,7 @@
 
 def make_uid(key):
     """forge a unique identifier"""
-    msg = str(key) + "%.10f"%time() + str(randint(0, 1000000))
+    msg = str(key) + "%.10f" % time() + str(randint(0, 1000000))
     return md5(msg).hexdigest()
 
 
--- a/view.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/view.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,10 +2,12 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
+_ = unicode
 
 from cStringIO import StringIO
 
@@ -18,8 +20,6 @@
 from cubicweb.appobject import AppRsetObject
 from cubicweb.utils import UStringIO, HTMLStream
 
-_ = unicode
-
 
 # robots control
 NOINDEX = u'<meta name="ROBOTS" content="NOINDEX" />'
@@ -206,7 +206,7 @@
 
     # view utilities ##########################################################
 
-    def wview(self, __vid, rset, __fallback_vid=None, **kwargs):
+    def wview(self, __vid, rset=None, __fallback_vid=None, **kwargs):
         """shortcut to self.view method automatically passing self.w as argument
         """
         self.view(__vid, rset, __fallback_vid, w=self.w, **kwargs)
@@ -221,21 +221,6 @@
         """simple helper that escapes `data` and writes into `self.w`"""
         self.w(html_escape(data))
 
-    def action(self, actionid, row=0):
-        """shortcut to get action object with id `actionid`"""
-        return self.vreg.select_action(actionid, self.req, self.rset,
-                                       row=row)
-
-    def action_url(self, actionid, label=None, row=0):
-        """simple method to be able to display `actionid` as a link anywhere
-        """
-        action = self.vreg.select_action(actionid, self.req, self.rset,
-                                         row=row)
-        if action:
-            label = label or self.req._(action.title)
-            return u'<a href="%s">%s</a>' % (html_escape(action.url()), label)
-        return u''
-
     def html_headers(self):
         """return a list of html headers (eg something to be inserted between
         <head> and </head> of the returned page
--- a/vregistry.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/vregistry.py	Wed Jun 03 09:15:20 2009 +0200
@@ -15,8 +15,9 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -237,7 +238,7 @@
                 # XXX automatic reloading management
                 try:
                     registry[obj.id].remove(registered)
-                except ValueError:
+                except KeyError:
                     self.warning('can\'t remove %s, no id %s in the %s registry',
                                  removed_id, obj.id, registryname)
                 except ValueError:
@@ -254,11 +255,14 @@
             replaced = replaced.classid()
         registryname = registryname or obj.__registry__
         registry = self.registry(registryname)
-        registered_objs = registry[obj.id]
+        registered_objs = registry.get(obj.id, ())
         for index, registered in enumerate(registered_objs):
             if registered.classid() == replaced:
                 del registry[obj.id][index]
                 break
+        else:
+            self.warning('trying to replace an unregistered view %s by %s',
+                         replaced, obj)
         self.register(obj, registryname=registryname)
 
     # dynamic selection methods ###############################################
--- a/web/__init__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/__init__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -3,8 +3,9 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 _ = unicode
@@ -13,6 +14,8 @@
 from datetime import datetime, date, timedelta
 from simplejson import dumps
 
+from logilab.common.deprecation import obsolete
+
 from cubicweb.common.uilib import urlquote
 from cubicweb.web._exceptions import *
 
@@ -62,6 +65,7 @@
         return json_dumps(function(*args, **kwargs))
     return newfunc
 
+@obsolete('use req.build_ajax_replace_url() instead')
 def ajax_replace_url(nodeid, rql, vid=None, swap=False, **extraparams):
     """builds a replacePageChunk-like url
     >>> ajax_replace_url('foo', 'Person P')
--- a/web/_exceptions.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/_exceptions.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 """exceptions used in the core of the CubicWeb web application
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -11,13 +12,13 @@
 
 class PublishException(CubicWebException):
     """base class for publishing related exception"""
-    
+
 class RequestError(PublishException):
     """raised when a request can't be served because of a bad input"""
 
 class NothingToEdit(RequestError):
     """raised when an edit request doesn't specify any eid to edit"""
-    
+
 class NotFound(RequestError):
     """raised when a 404 error should be returned"""
 
@@ -34,7 +35,7 @@
     def __init__(self, status, content=''):
         self.status = int(status)
         self.content = content
-    
+
 class ExplicitLogin(AuthenticationError):
     """raised when a bad connection id is given or when an attempt to establish
     a connection failed"""
@@ -55,4 +56,3 @@
     def dumps(self):
         import simplejson
         return simplejson.dumps({'reason': self.reason})
-        
--- a/web/action.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/action.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """abstract action classes for CubicWeb web client
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/web/application.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/application.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """CubicWeb web client application object
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/web/box.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/box.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """abstract box classes for CubicWeb web client
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -152,7 +153,7 @@
         entity = self.entity(row, col)
         limit = self.req.property_value('navigation.related-limit') + 1
         role = get_role(self)
-        self.w(u'<div class="sideRelated">')
+        self.w(u'<div class="sideBox">')
         self.wview('sidebox', entity.related(self.rtype, role, limit=limit),
                    title=display_name(self.req, self.rtype, role))
         self.w(u'</div>')
--- a/web/component.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/component.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """abstract component class and base components definition for CubicWeb web client
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/web/controller.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/controller.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,15 +2,15 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
 import datetime
 
 from cubicweb import typed_eid
-from cubicweb.utils import strptime, todate, todatetime
 from cubicweb.selectors import yes, require_group_compat
 from cubicweb.appobject import AppObject
 from cubicweb.web import LOGGER, Redirect, RequestError
@@ -107,35 +107,6 @@
             raise RequestError('missing required parameter(s): %s'
                                % ','.join(missing))
 
-    def parse_datetime(self, value, etype='Datetime'):
-        """get a datetime or time from a string (according to etype)
-        Datetime formatted as Date are accepted
-        """
-        assert etype in ('Datetime', 'Date', 'Time'), etype
-        # XXX raise proper validation error
-        if etype == 'Datetime':
-            format = self.req.property_value('ui.datetime-format')
-            try:
-                return todatetime(strptime(value, format))
-            except:
-                pass
-        elif etype == 'Time':
-            format = self.req.property_value('ui.time-format')
-            try:
-                # (adim) I can't find a way to parse a Time with a custom format
-                date = strptime(value, format) # this returns a DateTime
-                return datetime.time(date.hour, date.minute, date.second)
-            except:
-                raise ValueError('can\'t parse %r (expected %s)' % (value, format))
-        try:
-            format = self.req.property_value('ui.date-format')
-            dt = strptime(value, format)
-            if etype == 'Datetime':
-                return todatetime(dt)
-            return todate(dt)
-        except:
-            raise ValueError('can\'t parse %r (expected %s)' % (value, format))
-
 
     def notify_edited(self, entity):
         """called by edit_entity() to notify which entity is edited"""
Binary file web/data/black-uncheck.png has changed
--- a/web/data/cubicweb.ajax.js	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/data/cubicweb.ajax.js	Wed Jun 03 09:15:20 2009 +0200
@@ -317,6 +317,18 @@
     }
 }
 
+/*
+ * fetches `url` and replaces `nodeid`'s content with the result
+ * @param replacemode how the replacement should be done (default is 'replace')
+ *  Possible values are :
+ *    - 'replace' to replace the node's content with the generated HTML
+ *    - 'swap' to replace the node itself with the generated HTML
+ *    - 'append' to append the generated HTML to the node's content
+ */
+function loadxhtml(nodeid, url, /* ... */ replacemode) {
+    jQuery('#' + nodeid).loadxhtml(url, null, 'post', replacemode);
+}
+
 /* XXX: this function should go in edition.js but as for now, htmlReplace
  * references it.
  *
--- a/web/data/cubicweb.calendar.css	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/data/cubicweb.calendar.css	Wed Jun 03 09:15:20 2009 +0200
@@ -1,7 +1,7 @@
 /* styles for the calendar views
  *
  *  :organization: Logilab
- *  :copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+ *  :copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
  *  :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
  */
 
@@ -74,7 +74,7 @@
 }
 
 table.omcalendar tr td {
-  padding: 3px 0.5em 1em;                         
+  padding: 3px 0.5em 1em;
 }
 
 table.omcalendar tr td div.task {
@@ -82,11 +82,11 @@
   height: 2.5ex;
 }
 
-table.omcalendar tr td div.task div.tooltip { 
-  display: none; 
+table.omcalendar tr td div.task div.tooltip {
+  display: none;
 }
 
-table.omcalendar tr td:hover div.task:hover div.tooltip { 
+table.omcalendar tr td:hover div.task:hover div.tooltip {
   font-style: normal;
   display: block;
   position: absolute;
@@ -101,6 +101,7 @@
 
 
 table.omcalendar tr td.outOfRange { background-color: #ddd; }
+table.omcalendar tr td.today { border: 2px solid #2952A3; }
 
 
 table.omcalendar div.col0       { background-color: #FFB117; }
@@ -138,7 +139,7 @@
   min-height: 600px;
   width: 100%;
   table-layout: auto;
-  
+
 }
 
 
@@ -209,10 +210,10 @@
 /******************************************************************************/
 
 table.calendarPageHeader,
-table.smallCalendars, 
+table.smallCalendars,
  table.bigCalendars {
  width: 90%;
- border-collapse:separate; 
+ border-collapse:separate;
  margin: 0px 1em;
 }
 
@@ -232,20 +233,20 @@
  background: #cfceb7;
 }
 
-.calendar th.month a{ 
+.calendar th.month a{
  font: bold 110%  Georgia, Verdana;
  color : #fff;
  }
 
-table.weekCalendar{ 
+table.weekCalendar{
 }
 
-table.weekCalendar th{ 
+table.weekCalendar th{
  text-align : left;
  padding: 0.6em 0.4em;
  }
 
-table.weekCalendar td{ 
+table.weekCalendar td{
  padding: 0.2em 0.4em }
 
 .semesterCalendar .amCell, .semesterCalendar .amCellEmpty{
@@ -254,7 +255,7 @@
   border-right:thin dotted;
 }
 
-table.semesterCalendar th{ 
+table.semesterCalendar th{
  padding: 0.6em 0.4em;
 }
 
@@ -263,25 +264,25 @@
   border-left:1px dotted #ccc;
 }
 
-.weeknum{ 
+.weeknum{
  width:10%
  }
 
-.cell, .cellEmpty{ 
+.cell, .cellEmpty{
  border:1px solid #ccc;
  padding: 3px 0.5em 2em;
  width:10%;
 }
 
-.cellDay{ 
+.cellDay{
  border:1px solid #ccc;
  border-bottom : none;
- padding: 3px 0.5em 3px;  
+ padding: 3px 0.5em 3px;
  width:10%;}
 
 .amCell, .pmCell,
 .amCellEmpty, .pmCellEmpty{
- padding: 3px 0.5em 3px;  
+ padding: 3px 0.5em 3px;
  border:1px solid #ccc;
  border-top:none;
  width:10%;
@@ -309,22 +310,22 @@
 
 .cell span.cellTitle,
 .cellEmpty span.cellTitle {
- background-color:transparent; 
+ background-color:transparent;
 }
 
-div.cellContent{ 
+div.cellContent{
  padding: 0.1em;
  font-size:90%;
  }
 
-.weeknum, th.weekday{ 
+.weeknum, th.weekday{
  padding:0.2em 0.4em;
  color : #666;
  font-size:90%;}
 
-div.event{ 
+div.event{
  padding : 0.1em 0px;
- margin:0.2em 0px; 
+ margin:0.2em 0px;
  background: #eee;
 }
 
--- a/web/data/cubicweb.css	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/data/cubicweb.css	Wed Jun 03 09:15:20 2009 +0200
@@ -455,6 +455,8 @@
   padding: 0.2em 0px;
   margin-bottom: 0.5em;
   background: #eeedd9;
+  min-width: 21em;
+  max-width: 50em;
 }
 
 ul.sideBox li{ 
@@ -572,24 +574,11 @@
   padding: 0.5em 0.2em 0.2em;
 }
 
-div.sideRelated h4,
-div.sideRelated h5 {
-  margin-top: 0px;
-  margin-bottom: 0px;
-}
-
 div.primaryRight{
   float:right;
   
  }
 
-div.sideRelated {
-  margin-right: 1em;
-  padding: 12px 0px 12px 12px;
-  min-width: 21em;
-  max-width: 50em;
-}
-
 div.metadata {
   font-size: 90%;
   margin: 5px 0px 3px;
--- a/web/data/cubicweb.edition.js	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/data/cubicweb.edition.js	Wed Jun 03 09:15:20 2009 +0200
@@ -342,11 +342,11 @@
 }
 
 
-function handleFormValidationResponse(formid, onsuccess, result) {
+function handleFormValidationResponse(formid, onsuccess, onfailure, result) {
     // Success
     if (result[0]) {
 	if (onsuccess) {
-	    return onsuccess(result[1]);
+	    return onsuccess(result[1], formid);
 	} else {
 	    document.location.href = result[1];
 	    return ;
@@ -365,6 +365,9 @@
     _displayValidationerrors(formid, descr[0], descr[1]);
     updateMessage(_("please correct errors below"));
     document.location.hash = '#header';
+    if (onfailure){
+	onfailure(formid);
+    }
     return false;
 }
 
@@ -426,7 +429,7 @@
  * to the appropriate URL. Otherwise, the validation errors are displayed
  * around the corresponding input fields.
  */
-function validateForm(formid, action, onsuccess) {
+function validateForm(formid, action, onsuccess, onfailure) {
     try {
 	var zipped = formContents(formid);
 	var d = asyncRemoteExec('validate_form', action, zipped[0], zipped[1]);
@@ -435,7 +438,7 @@
 	return false;
     }
     function _callback(result, req) {
-	handleFormValidationResponse(formid, onsuccess, result);
+	handleFormValidationResponse(formid, onsuccess, onfailure, result);
     }
     d.addCallback(_callback);
     return false;
@@ -468,7 +471,7 @@
 	return false;
     }
     d.addCallback(function (result, req) {
-        handleFormValidationResponse(formid, noop, result);
+    handleFormValidationResponse(formid, noop, noop, result);
 	if (reload) {
 	    document.location.href = result[1];
 	} else {
@@ -502,7 +505,7 @@
 	return false;
     }
     d.addCallback(function (result, req) {
-        handleFormValidationResponse(formid, noop, result);
+        handleFormValidationResponse(formid, noop, noop, result);
 	var fieldview = getNode(divid);
         fieldview.innerHTML = result[2];
 	// switch inline form off only if no error
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/data/cubicweb.flot.js	Wed Jun 03 09:15:20 2009 +0200
@@ -0,0 +1,34 @@
+function showTooltip(x, y, contents) {
+    $('<div id="tooltip">' + contents + '</div>').css( {
+            position: 'absolute',
+	    display: 'none',
+	    top: y + 5,
+            left: x + 5,
+            border: '1px solid #fdd',
+            padding: '2px',
+            'background-color': '#fee',
+            opacity: 0.80
+		}).appendTo("body").fadeIn(200);
+}
+
+var previousPoint = null;
+function onPlotHover(event, pos, item) {
+    if (item) {
+        if (previousPoint != item.datapoint) {
+    	previousPoint = item.datapoint;
+    	
+    	$("#tooltip").remove();
+    	var x = item.datapoint[0].toFixed(2),
+    	    y = item.datapoint[1].toFixed(2);
+	if (item.datapoint.length == 3) {
+	    var x = new Date(item.datapoint[2]);
+	    x = x.toLocaleDateString() + ' ' + x.toLocaleTimeString();
+	}
+    	showTooltip(item.pageX, item.pageY,
+    		    item.series.label + ': (' + x + ' ; ' + y + ')');
+            }
+    } else {
+        $("#tooltip").remove();
+        previousPoint = null;
+    }
+}
--- a/web/data/cubicweb.formfilter.js	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/data/cubicweb.formfilter.js	Wed Jun 03 09:15:20 2009 +0200
@@ -8,7 +8,6 @@
 CubicWeb.require('ajax.js');
 
 //============= filter form functions ========================================//
-
 function copyParam(origparams, newparams, param) {
     var index = findValue(origparams[0], param);
     if (index > -1) {
@@ -30,7 +29,7 @@
         names.push(this.name);
         values.push(this.value);
     });
-    jQuery(form).find('select option[@selected]').each(function () {
+    jQuery(form).find('select option[selected]').each(function () {
 	names.push(this.parentNode.name);
 	values.push(this.value);
     });
@@ -104,7 +103,7 @@
 
 var SELECTED_IMG = baseuri()+"data/black-check.png";
 var UNSELECTED_IMG = baseuri()+"data/no-check-no-border.png";
-var UNSELECTED_BORDER_IMG = baseuri()+"data/black-unchecked.png";
+var UNSELECTED_BORDER_IMG = baseuri()+"data/black-uncheck.png";
 
 function initFacetBoxEvents(root) {
     // facetargs : (divid, vid, paginate, extraargs)
@@ -134,7 +133,7 @@
 			       this.setAttribute('src', UNSELECTED_BORDER_IMG);
 			    }
 			    else{
-                             this.setAttribute('src', UNSELECTED_IMG);
+			       this.setAttribute('src', UNSELECTED_IMG);
 			    }
 			});
 			var index = parseInt($this.attr('cubicweb:idx'));
@@ -144,7 +143,11 @@
 			}).length;
 			index += shift;
 			var parent = this.parentNode;
-			jQuery(parent).find('.facetCheckBox:nth('+index+')').after(this);
+			var $insertAfter = jQuery(parent).find('.facetCheckBox:nth('+index+')');
+			if ( ! ($insertAfter.length == 1 && index == 0) ) {
+			    // only rearrange element if necessary
+			    $insertAfter.after(this);
+			}
 		    } else {
 			var lastSelected = facet.find('.facetValueSelected:last');
 			if (lastSelected.length) {
--- a/web/data/cubicweb.preferences.css	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/data/cubicweb.preferences.css	Wed Jun 03 09:15:20 2009 +0200
@@ -6,98 +6,105 @@
  */
 
 
-table.preferences td{ 
- padding: 0 0.5em 1em;
+.preferences .validateButton{
+ margin-top:0px;
  }
 
-fieldset.preferences{  
+fieldset.preferences{
  border : 1px solid #CFCEB7;
- margin:1em 0;
- padding:0 1em 1em;
+ margin:7px 1em 0;
+ padding:2px 6px 6px;
 }
 
-div.preffield {
- margin-bottom: 0.8em ;
+div.component {
+ margin-left: 1em;
 }
 
-/*
-div.preffield label{ 
- font-size:110%
- }
-*/
-
-div.prefinput{ 
- margin:.3em 0;
-}
-
-div.componentLink{ 
+div.componentLink{
  margin-top:0.3em;
  }
 
 a.componentTitle{
  font-weight:bold;
- color: #000;
+ color: #000/*#0083AB;*/
  }
 
 a.componentTitle:visited{
  color: #000;
 }
 
-
-
 h2.propertiesform a{
  display:block;
  margin: 10px 0px 6px 0px;
  font-weight: bold;
- color: #222211;
+ color: #000;
  padding: 0.2em 0.2em 0.2em 16px;
  background:#eeedd9 url("puce_down.png") 3px center no-repeat;
- font-size:76%;
+ font-size:89%;
 }
 
 h2.propertiesform a:hover{
- color:#000;
  background-color:#cfceb7;
+}
+
+h2.propertiesform a:hover,
+h2.propertiesform a:visited{
  text-decoration:none;
+ color: #000;
 }
 
+div.preffield {
+ margin-bottom: 5px;
+ padding:2px 5px;
+ background:#eeedd9;
+}
+
+div.prefinput{
+ margin:.3em;
+}
+
+
 div.prefinput select.changed,
-div.prefinput input.changed{ 
- background:#eeedd9;
- border: 1px solid #eeedd9;
+div.prefinput input.changed{
+ border: 1px solid #000;
+ font-weight:bold;
+
 }
 
 div.prefinput select,
-div.prefinput input{ 
+div.prefinput input{
  background:#fff;
  border: 1px solid #CFCEB7;
 }
 
 .prefinput input.error {
- background:transparent url(error.png) no-repeat scroll 100% 50% !important;
+ /* background:#fff url(error.png) no-repeat scroll 100% 50% !important; */
+ border:1px solid red !important;
+ color:red;
+ padding-right:1em;
 }
 
 
-div.formsg{ 
+div.formsg{
  font-weight:bold;
  margin:0.5em 0px;
- }
+}
 
 
-div.formsg .critical{ 
+div.critical{
  color:red;
  padding-left:20px;
  background:#fff url(critical.png) no-repeat;
  }
 
-div.formsg .message{ 
+div.formsg .msg{
  color : green;
 }
 
 .helper{
   font-size: 96%;
   color: #555544;
-  padding:0; 
+  padding:0;
 }
 
 div.prefinput .helper:hover {
@@ -105,11 +112,6 @@
   cursor: default;
 }
 
-.error{ 
- color:red;
- padding-right:1em;
+div.openlink{
+ display:inline;
  }
-
-div.openlink{ 
- display:inline;
- }
\ No newline at end of file
--- a/web/data/cubicweb.preferences.js	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/data/cubicweb.preferences.js	Wed Jun 03 09:15:20 2009 +0200
@@ -4,23 +4,21 @@
  *     move me in a more appropriate place
  */
 
-function toggleVisibility(elemId, cookiename) {
-    _clearPreviousMessages();
-    jqNode(elemId).toggleClass('hidden');
-    asyncRemoteExec('set_cookie', cookiename,
-                      jQuery('#' + elemId).attr('class'));
+function togglePrefVisibility(elemId) {
+    clearPreviousMessages();
+    jQuery('#' + elemId).toggleClass('hidden');
 }
 
 function closeFieldset(fieldsetid){
     var linklabel = _('open all');
-    var linkhref = 'javascript:openFieldset("' +fieldsetid + '")'
-    _toggleFieldset(fieldsetid, 1, linklabel, linkhref)
+    var linkhref = 'javascript:openFieldset("' +fieldsetid + '")';
+    _toggleFieldset(fieldsetid, 1, linklabel, linkhref);
 }
 
 function openFieldset(fieldsetid){
     var linklabel = _('close all');
-    var linkhref = 'javascript:closeFieldset("'+ fieldsetid + '")'
-    _toggleFieldset(fieldsetid, 0, linklabel, linkhref)
+    var linkhref = 'javascript:closeFieldset("'+ fieldsetid + '")';
+    _toggleFieldset(fieldsetid, 0, linklabel, linkhref);
 }
 
 
@@ -28,13 +26,13 @@
     jQuery('#'+fieldsetid).find('div.openlink').each(function(){
 	    var link = A({'href' : "javascript:noop();",
 			  'onclick' : linkhref},
-			  linklabel)
+			  linklabel);
 	    jQuery(this).empty().append(link);
 	});
     jQuery('#'+fieldsetid).find('fieldset[id]').each(function(){
 	    var fieldset = jQuery(this);
 	    if(closeaction){
-		fieldset.addClass('hidden')
+		fieldset.addClass('hidden');
 	    }else{
 		fieldset.removeClass('hidden');
 		linkLabel = (_('open all'));
@@ -43,44 +41,24 @@
 }
 
 function validatePrefsForm(formid){
-    var form = getNode(formid);
-    freezeFormButtons(formid);
-    try {
-	var d = _sendForm(formid, null);
-    } catch (ex) {
-	log('got exception', ex);
-	return false;
-    }
-    function _callback(result, req) {
-	_clearPreviousMessages();
-	_clearPreviousErrors(formid);
-	// success
-	if(result[0]){
-	    return submitSucces(formid)
-	}
- 	// Failures
-	unfreezeFormButtons(formid);
-	var descr = result[1];
-        if (!isArrayLike(descr) || descr.length != 2) {
-	   log('got strange error :', descr);
-	   updateMessage(descr);
-	   return ;
-	}
-        _displayValidationerrors(formid, descr[0], descr[1]);
-	var dom = DIV({'class':'critical'},
-		      _("please correct errors below"));
-	jQuery(form).find('div.formsg').empty().append(dom);
-	updateMessage(_(""));
-	return false;
-    }
-    d.addCallback(_callback);
-    return false;
+    clearPreviousMessages();
+    clearPreviousErrors(formid);
+    return validateForm(formid, null,  submitSucces, submitFailure);
 }
 
-function submitSucces(formid){
+function submitFailure(formid){
+    var form = jQuery('#'+formid);
+    var dom = DIV({'class':'critical'},
+		  _("please correct errors below"));
+    jQuery(form).find('div.formsg').empty().append(dom);
+    // clearPreviousMessages()
+    jQuery(form).find('span.error').next().focus();
+}
+
+function submitSucces(url, formid){
     var form = jQuery('#'+formid);
     setCurrentValues(form);
-    var dom = DIV({'class':'message'},
+    var dom = DIV({'class':'msg'},
 		  _("changes applied"));
     jQuery(form).find('div.formsg').empty().append(dom);
     jQuery(form).find('input').removeClass('changed');
@@ -88,87 +66,87 @@
     return;
 }
 
-function _clearPreviousMessages() {
+function clearPreviousMessages() {
     jQuery('div#appMsg').addClass('hidden');
     jQuery('div.formsg').empty();
 }
 
-function _clearPreviousErrors(formid) {
-    jQuery('#' + formid + ' span.error').remove();
+function clearPreviousErrors(formid) {
+    jQuery('#err-value:' + formid).remove();
 }
 
 
 function checkValues(form, success){
     var unfreezeButtons = false;
-    jQuery(form).find('select').each(function () { 
+    jQuery(form).find('select').each(function () {
 	    unfreezeButtons = _checkValue(jQuery(this), unfreezeButtons);
 	});
     jQuery(form).find('[type=text]').each(function () {
 	    unfreezeButtons = _checkValue(jQuery(this), unfreezeButtons);
 	});
-    jQuery(form).find('input[type=radio]').each(function () { 
-	    if (jQuery(this).attr('checked')){
-		unfreezeButtons = _checkValue(jQuery(this), unfreezeButtons);
-	    }
-     }); 
-   
+    jQuery(form).find('input[type=radio]:checked').each(function () {
+            unfreezeButtons = _checkValue(jQuery(this), unfreezeButtons);
+     });
+
     if (unfreezeButtons){
 	unfreezeFormButtons(form.attr('id'));
     }else{
 	if (!success){
-	    _clearPreviousMessages();
+	    clearPreviousMessages();
 	}
-	_clearPreviousErrors(form.attr('id'));
+	clearPreviousErrors(form.attr('id'));
 	freezeFormButtons(form.attr('id'));
     }
 }
 
 function _checkValue(input, unfreezeButtons){
-     var currentValueInput = jQuery("input[id=current-" + input.attr('name') + "]");
-     if (currentValueInput.attr('value') != input.attr('value')){
+     var currentValueInput = jQuery("input[name=current-" + input.attr('name') + "]");
+     if (currentValueInput.val() != input.val()){
 	 input.addClass('changed');
 	 unfreezeButtons = true;
      }else{
 	 input.removeClass('changed');
 	 jQuery("span[id=err-" + input.attr('id') + "]").remove();
-     }	
+     }
      input.removeClass('error');
-     return unfreezeButtons
+     return unfreezeButtons;
 }
 
 
 function setCurrentValues(form){
-    jQuery(form).find('input[id^=current-value]').each(function () { 
+    jQuery(form).find('input[name^=current-value]').each(function () {
 	    var currentValueInput = jQuery(this);
-	    var name = currentValueInput.attr('id').split('-')[1];
+	    var name = currentValueInput.attr('name').split('-')[1];
 	    jQuery(form).find("[name=" + name + "]").each(function (){
 		    var input = jQuery(this);
-		    if(input.attr('type')=='radio'){
-			if(input.attr('checked')){
-			    log(input.attr('value'));
-			    currentValueInput.attr('value', input.attr('value'));
+		    if(input.attr('type') == 'radio'){
+			// NOTE: there seems to be a bug with jQuery(input).attr('checked')
+			//       in our case, we can't rely on its value, we use
+			//       the DOM API instead.
+			if(input[0].checked){
+			    currentValueInput.val(input.val());
 			}
 		    }else{
-			currentValueInput.attr('value', input.attr('value'));
+			currentValueInput.val(input.val());
 		    }
 		});
     });
 }
 
-
 function initEvents(){
-  jQuery('form').each(function() { 
+  jQuery('form').each(function() {
 	  var form = jQuery(this);
 	  freezeFormButtons(form.attr('id'));
-	  form.find('input[type=text]').keyup(function(){  
-		  checkValues(form);	   
+	  form.find('input[type=text]').keyup(function(){
+		  checkValues(form);
           });
-	  form.find('input[type=radio]').change(function(){  
-		  checkValues(form);	   
+	  form.find('input[type=radio]').change(function(){
+		  checkValues(form);
           });
-	  form.find('select').change(function(){  
-		  checkValues(form);	 
+	  form.find('select').change(function(){
+		  checkValues(form);
           });
+	  setCurrentValues(form);
     });
 }
 
--- a/web/data/cubicweb.python.js	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/data/cubicweb.python.js	Wed Jun 03 09:15:20 2009 +0200
@@ -21,23 +21,23 @@
     var res = new Date()
     res.setTime(this.getTime() + (days * ONE_DAY))
     return res
-}
+};
 
 Date.prototype.sub = function(days) {
     return this.add(-days);
-}
+};
 
 Date.prototype.iadd = function(days) {
     // in-place add
     this.setTime(this.getTime() + (days * ONE_DAY))
     // avoid strange rounding problems !!
     this.setHours(12);
-}
+};
 
 Date.prototype.isub = function(days) {
     // in-place sub
     this.setTime(this.getTime() - (days * ONE_DAY))
-}
+};
 
 /*
  * returns the first day of the next month
@@ -50,7 +50,7 @@
 	var d2 = new Date(this.getFullYear(), this.getMonth()+1, 1);
 	return d2;
     }
-}
+};
 
 /*
  * returns the day of week, 0 being monday, 6 being sunday
@@ -58,8 +58,15 @@
 Date.prototype.getRealDay = function() {
     // getDay() returns 0 for Sunday ==> 6 for Saturday
     return (this.getDay()+6) % 7;
-}
+};
 
+Date.prototype.strftime = function(fmt) {
+    if (this.toLocaleFormat !== undefined) { // browser dependent
+	return this.toLocaleFormat(fmt);
+    }
+    // XXX implement at least a decent fallback implementation
+    return this.getFullYear() + '/' + (this.getMonth()+1) + '/' + this.getDate();
+};
 
 var _DATE_FORMAT_REGXES = {
     'Y': new RegExp('^-?[0-9]+'),
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/data/excanvas.js	Wed Jun 03 09:15:20 2009 +0200
@@ -0,0 +1,785 @@
+// Copyright 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+// Known Issues:
+//
+// * Patterns are not implemented.
+// * Radial gradient are not implemented. The VML version of these look very
+//   different from the canvas one.
+// * Clipping paths are not implemented.
+// * Coordsize. The width and height attribute have higher priority than the
+//   width and height style values which isn't correct.
+// * Painting mode isn't implemented.
+// * Canvas width/height should is using content-box by default. IE in
+//   Quirks mode will draw the canvas using border-box. Either change your
+//   doctype to HTML5
+//   (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)
+//   or use Box Sizing Behavior from WebFX
+//   (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)
+// * Optimize. There is always room for speed improvements.
+
+// only add this code if we do not already have a canvas implementation
+if (!window.CanvasRenderingContext2D) {
+
+(function () {
+
+  // alias some functions to make (compiled) code shorter
+  var m = Math;
+  var mr = m.round;
+  var ms = m.sin;
+  var mc = m.cos;
+
+  // this is used for sub pixel precision
+  var Z = 10;
+  var Z2 = Z / 2;
+
+  var G_vmlCanvasManager_ = {
+    init: function (opt_doc) {
+      var doc = opt_doc || document;
+      if (/MSIE/.test(navigator.userAgent) && !window.opera) {
+        var self = this;
+        doc.attachEvent("onreadystatechange", function () {
+          self.init_(doc);
+        });
+      }
+    },
+
+    init_: function (doc) {
+      if (doc.readyState == "complete") {
+        // create xmlns
+        if (!doc.namespaces["g_vml_"]) {
+          doc.namespaces.add("g_vml_", "urn:schemas-microsoft-com:vml");
+        }
+
+        // setup default css
+        var ss = doc.createStyleSheet();
+        ss.cssText = "canvas{display:inline-block;overflow:hidden;" +
+            // default size is 300x150 in Gecko and Opera
+            "text-align:left;width:300px;height:150px}" +
+            "g_vml_\\:*{behavior:url(#default#VML)}";
+
+        // find all canvas elements
+        var els = doc.getElementsByTagName("canvas");
+        for (var i = 0; i < els.length; i++) {
+          if (!els[i].getContext) {
+            this.initElement(els[i]);
+          }
+        }
+      }
+    },
+
+    fixElement_: function (el) {
+      // in IE before version 5.5 we would need to add HTML: to the tag name
+      // but we do not care about IE before version 6
+      var outerHTML = el.outerHTML;
+
+      var newEl = el.ownerDocument.createElement(outerHTML);
+      // if the tag is still open IE has created the children as siblings and
+      // it has also created a tag with the name "/FOO"
+      if (outerHTML.slice(-2) != "/>") {
+        var tagName = "/" + el.tagName;
+        var ns;
+        // remove content
+        while ((ns = el.nextSibling) && ns.tagName != tagName) {
+          ns.removeNode();
+        }
+        // remove the incorrect closing tag
+        if (ns) {
+          ns.removeNode();
+        }
+      }
+      el.parentNode.replaceChild(newEl, el);
+      return newEl;
+    },
+
+    /**
+     * Public initializes a canvas element so that it can be used as canvas
+     * element from now on. This is called automatically before the page is
+     * loaded but if you are creating elements using createElement you need to
+     * make sure this is called on the element.
+     * @param {HTMLElement} el The canvas element to initialize.
+     * @return {HTMLElement} the element that was created.
+     */
+    initElement: function (el) {
+      el = this.fixElement_(el);
+      el.getContext = function () {
+        if (this.context_) {
+          return this.context_;
+        }
+        return this.context_ = new CanvasRenderingContext2D_(this);
+      };
+
+      // do not use inline function because that will leak memory
+      el.attachEvent('onpropertychange', onPropertyChange);
+      el.attachEvent('onresize', onResize);
+
+      var attrs = el.attributes;
+      if (attrs.width && attrs.width.specified) {
+        // TODO: use runtimeStyle and coordsize
+        // el.getContext().setWidth_(attrs.width.nodeValue);
+        el.style.width = attrs.width.nodeValue + "px";
+      } else {
+        el.width = el.clientWidth;
+      }
+      if (attrs.height && attrs.height.specified) {
+        // TODO: use runtimeStyle and coordsize
+        // el.getContext().setHeight_(attrs.height.nodeValue);
+        el.style.height = attrs.height.nodeValue + "px";
+      } else {
+        el.height = el.clientHeight;
+      }
+      //el.getContext().setCoordsize_()
+      return el;
+    }
+  };
+
+  function onPropertyChange(e) {
+    var el = e.srcElement;
+
+    switch (e.propertyName) {
+      case 'width':
+        el.style.width = el.attributes.width.nodeValue + "px";
+        el.getContext().clearRect();
+        break;
+      case 'height':
+        el.style.height = el.attributes.height.nodeValue + "px";
+        el.getContext().clearRect();
+        break;
+    }
+  }
+
+  function onResize(e) {
+    var el = e.srcElement;
+    if (el.firstChild) {
+      el.firstChild.style.width =  el.clientWidth + 'px';
+      el.firstChild.style.height = el.clientHeight + 'px';
+    }
+  }
+
+  G_vmlCanvasManager_.init();
+
+  // precompute "00" to "FF"
+  var dec2hex = [];
+  for (var i = 0; i < 16; i++) {
+    for (var j = 0; j < 16; j++) {
+      dec2hex[i * 16 + j] = i.toString(16) + j.toString(16);
+    }
+  }
+
+  function createMatrixIdentity() {
+    return [
+      [1, 0, 0],
+      [0, 1, 0],
+      [0, 0, 1]
+    ];
+  }
+
+  function matrixMultiply(m1, m2) {
+    var result = createMatrixIdentity();
+
+    for (var x = 0; x < 3; x++) {
+      for (var y = 0; y < 3; y++) {
+        var sum = 0;
+
+        for (var z = 0; z < 3; z++) {
+          sum += m1[x][z] * m2[z][y];
+        }
+
+        result[x][y] = sum;
+      }
+    }
+    return result;
+  }
+
+  function copyState(o1, o2) {
+    o2.fillStyle     = o1.fillStyle;
+    o2.lineCap       = o1.lineCap;
+    o2.lineJoin      = o1.lineJoin;
+    o2.lineWidth     = o1.lineWidth;
+    o2.miterLimit    = o1.miterLimit;
+    o2.shadowBlur    = o1.shadowBlur;
+    o2.shadowColor   = o1.shadowColor;
+    o2.shadowOffsetX = o1.shadowOffsetX;
+    o2.shadowOffsetY = o1.shadowOffsetY;
+    o2.strokeStyle   = o1.strokeStyle;
+    o2.arcScaleX_    = o1.arcScaleX_;
+    o2.arcScaleY_    = o1.arcScaleY_;
+  }
+
+  function processStyle(styleString) {
+    var str, alpha = 1;
+
+    styleString = String(styleString);
+    if (styleString.substring(0, 3) == "rgb") {
+      var start = styleString.indexOf("(", 3);
+      var end = styleString.indexOf(")", start + 1);
+      var guts = styleString.substring(start + 1, end).split(",");
+
+      str = "#";
+      for (var i = 0; i < 3; i++) {
+        str += dec2hex[Number(guts[i])];
+      }
+
+      if ((guts.length == 4) && (styleString.substr(3, 1) == "a")) {
+        alpha = guts[3];
+      }
+    } else {
+      str = styleString;
+    }
+
+    return [str, alpha];
+  }
+
+  function processLineCap(lineCap) {
+    switch (lineCap) {
+      case "butt":
+        return "flat";
+      case "round":
+        return "round";
+      case "square":
+      default:
+        return "square";
+    }
+  }
+
+  /**
+   * This class implements CanvasRenderingContext2D interface as described by
+   * the WHATWG.
+   * @param {HTMLElement} surfaceElement The element that the 2D context should
+   * be associated with
+   */
+   function CanvasRenderingContext2D_(surfaceElement) {
+    this.m_ = createMatrixIdentity();
+
+    this.mStack_ = [];
+    this.aStack_ = [];
+    this.currentPath_ = [];
+
+    // Canvas context properties
+    this.strokeStyle = "#000";
+    this.fillStyle = "#000";
+
+    this.lineWidth = 1;
+    this.lineJoin = "miter";
+    this.lineCap = "butt";
+    this.miterLimit = Z * 1;
+    this.globalAlpha = 1;
+    this.canvas = surfaceElement;
+
+    var el = surfaceElement.ownerDocument.createElement('div');
+    el.style.width =  surfaceElement.clientWidth + 'px';
+    el.style.height = surfaceElement.clientHeight + 'px';
+    el.style.overflow = 'hidden';
+    el.style.position = 'absolute';
+    surfaceElement.appendChild(el);
+
+    this.element_ = el;
+    this.arcScaleX_ = 1;
+    this.arcScaleY_ = 1;
+  }
+
+  var contextPrototype = CanvasRenderingContext2D_.prototype;
+  contextPrototype.clearRect = function() {
+    this.element_.innerHTML = "";
+    this.currentPath_ = [];
+  };
+
+  contextPrototype.beginPath = function() {
+    // TODO: Branch current matrix so that save/restore has no effect
+    //       as per safari docs.
+
+    this.currentPath_ = [];
+  };
+
+  contextPrototype.moveTo = function(aX, aY) {
+    this.currentPath_.push({type: "moveTo", x: aX, y: aY});
+    this.currentX_ = aX;
+    this.currentY_ = aY;
+  };
+
+  contextPrototype.lineTo = function(aX, aY) {
+    this.currentPath_.push({type: "lineTo", x: aX, y: aY});
+    this.currentX_ = aX;
+    this.currentY_ = aY;
+  };
+
+  contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
+                                            aCP2x, aCP2y,
+                                            aX, aY) {
+    this.currentPath_.push({type: "bezierCurveTo",
+                           cp1x: aCP1x,
+                           cp1y: aCP1y,
+                           cp2x: aCP2x,
+                           cp2y: aCP2y,
+                           x: aX,
+                           y: aY});
+    this.currentX_ = aX;
+    this.currentY_ = aY;
+  };
+
+  contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
+    // the following is lifted almost directly from
+    // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes
+    var cp1x = this.currentX_ + 2.0 / 3.0 * (aCPx - this.currentX_);
+    var cp1y = this.currentY_ + 2.0 / 3.0 * (aCPy - this.currentY_);
+    var cp2x = cp1x + (aX - this.currentX_) / 3.0;
+    var cp2y = cp1y + (aY - this.currentY_) / 3.0;
+    this.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, aX, aY);
+  };
+
+  contextPrototype.arc = function(aX, aY, aRadius,
+                                  aStartAngle, aEndAngle, aClockwise) {
+    aRadius *= Z;
+    var arcType = aClockwise ? "at" : "wa";
+
+    var xStart = aX + (mc(aStartAngle) * aRadius) - Z2;
+    var yStart = aY + (ms(aStartAngle) * aRadius) - Z2;
+
+    var xEnd = aX + (mc(aEndAngle) * aRadius) - Z2;
+    var yEnd = aY + (ms(aEndAngle) * aRadius) - Z2;
+
+    // IE won't render arches drawn counter clockwise if xStart == xEnd.
+    if (xStart == xEnd && !aClockwise) {
+      xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
+                       // that can be represented in binary
+    }
+
+    this.currentPath_.push({type: arcType,
+                           x: aX,
+                           y: aY,
+                           radius: aRadius,
+                           xStart: xStart,
+                           yStart: yStart,
+                           xEnd: xEnd,
+                           yEnd: yEnd});
+
+  };
+
+  contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
+    this.moveTo(aX, aY);
+    this.lineTo(aX + aWidth, aY);
+    this.lineTo(aX + aWidth, aY + aHeight);
+    this.lineTo(aX, aY + aHeight);
+    this.closePath();
+  };
+
+  contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
+    // Will destroy any existing path (same as FF behaviour)
+    this.beginPath();
+    this.moveTo(aX, aY);
+    this.lineTo(aX + aWidth, aY);
+    this.lineTo(aX + aWidth, aY + aHeight);
+    this.lineTo(aX, aY + aHeight);
+    this.closePath();
+    this.stroke();
+  };
+
+  contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
+    // Will destroy any existing path (same as FF behaviour)
+    this.beginPath();
+    this.moveTo(aX, aY);
+    this.lineTo(aX + aWidth, aY);
+    this.lineTo(aX + aWidth, aY + aHeight);
+    this.lineTo(aX, aY + aHeight);
+    this.closePath();
+    this.fill();
+  };
+
+  contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
+    var gradient = new CanvasGradient_("gradient");
+    return gradient;
+  };
+
+  contextPrototype.createRadialGradient = function(aX0, aY0,
+                                                   aR0, aX1,
+                                                   aY1, aR1) {
+    var gradient = new CanvasGradient_("gradientradial");
+    gradient.radius1_ = aR0;
+    gradient.radius2_ = aR1;
+    gradient.focus_.x = aX0;
+    gradient.focus_.y = aY0;
+    return gradient;
+  };
+
+  contextPrototype.drawImage = function (image, var_args) {
+    var dx, dy, dw, dh, sx, sy, sw, sh;
+
+    // to find the original width we overide the width and height
+    var oldRuntimeWidth = image.runtimeStyle.width;
+    var oldRuntimeHeight = image.runtimeStyle.height;
+    image.runtimeStyle.width = 'auto';
+    image.runtimeStyle.height = 'auto';
+
+    // get the original size
+    var w = image.width;
+    var h = image.height;
+
+    // and remove overides
+    image.runtimeStyle.width = oldRuntimeWidth;
+    image.runtimeStyle.height = oldRuntimeHeight;
+
+    if (arguments.length == 3) {
+      dx = arguments[1];
+      dy = arguments[2];
+      sx = sy = 0;
+      sw = dw = w;
+      sh = dh = h;
+    } else if (arguments.length == 5) {
+      dx = arguments[1];
+      dy = arguments[2];
+      dw = arguments[3];
+      dh = arguments[4];
+      sx = sy = 0;
+      sw = w;
+      sh = h;
+    } else if (arguments.length == 9) {
+      sx = arguments[1];
+      sy = arguments[2];
+      sw = arguments[3];
+      sh = arguments[4];
+      dx = arguments[5];
+      dy = arguments[6];
+      dw = arguments[7];
+      dh = arguments[8];
+    } else {
+      throw "Invalid number of arguments";
+    }
+
+    var d = this.getCoords_(dx, dy);
+
+    var w2 = sw / 2;
+    var h2 = sh / 2;
+
+    var vmlStr = [];
+
+    var W = 10;
+    var H = 10;
+
+    // For some reason that I've now forgotten, using divs didn't work
+    vmlStr.push(' <g_vml_:group',
+                ' coordsize="', Z * W, ',', Z * H, '"',
+                ' coordorigin="0,0"' ,
+                ' style="width:', W, ';height:', H, ';position:absolute;');
+
+    // If filters are necessary (rotation exists), create them
+    // filters are bog-slow, so only create them if abbsolutely necessary
+    // The following check doesn't account for skews (which don't exist
+    // in the canvas spec (yet) anyway.
+
+    if (this.m_[0][0] != 1 || this.m_[0][1]) {
+      var filter = [];
+
+      // Note the 12/21 reversal
+      filter.push("M11='", this.m_[0][0], "',",
+                  "M12='", this.m_[1][0], "',",
+                  "M21='", this.m_[0][1], "',",
+                  "M22='", this.m_[1][1], "',",
+                  "Dx='", mr(d.x / Z), "',",
+                  "Dy='", mr(d.y / Z), "'");
+
+      // Bounding box calculation (need to minimize displayed area so that
+      // filters don't waste time on unused pixels.
+      var max = d;
+      var c2 = this.getCoords_(dx + dw, dy);
+      var c3 = this.getCoords_(dx, dy + dh);
+      var c4 = this.getCoords_(dx + dw, dy + dh);
+
+      max.x = Math.max(max.x, c2.x, c3.x, c4.x);
+      max.y = Math.max(max.y, c2.y, c3.y, c4.y);
+
+      vmlStr.push("padding:0 ", mr(max.x / Z), "px ", mr(max.y / Z),
+                  "px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",
+                  filter.join(""), ", sizingmethod='clip');");
+    } else {
+      vmlStr.push("top:", mr(d.y / Z), "px;left:", mr(d.x / Z), "px;");
+    }
+
+    vmlStr.push(' ">' ,
+                '<g_vml_:image src="', image.src, '"',
+                ' style="width:', Z * dw, ';',
+                ' height:', Z * dh, ';"',
+                ' cropleft="', sx / w, '"',
+                ' croptop="', sy / h, '"',
+                ' cropright="', (w - sx - sw) / w, '"',
+                ' cropbottom="', (h - sy - sh) / h, '"',
+                ' />',
+                '</g_vml_:group>');
+
+    this.element_.insertAdjacentHTML("BeforeEnd",
+                                    vmlStr.join(""));
+  };
+
+  contextPrototype.stroke = function(aFill) {
+    var lineStr = [];
+    var lineOpen = false;
+    var a = processStyle(aFill ? this.fillStyle : this.strokeStyle);
+    var color = a[0];
+    var opacity = a[1] * this.globalAlpha;
+
+    var W = 10;
+    var H = 10;
+
+    lineStr.push('<g_vml_:shape',
+                 ' fillcolor="', color, '"',
+                 ' filled="', Boolean(aFill), '"',
+                 ' style="position:absolute;width:', W, ';height:', H, ';"',
+                 ' coordorigin="0 0" coordsize="', Z * W, ' ', Z * H, '"',
+                 ' stroked="', !aFill, '"',
+                 ' strokeweight="', this.lineWidth, '"',
+                 ' strokecolor="', color, '"',
+                 ' path="');
+
+    var newSeq = false;
+    var min = {x: null, y: null};
+    var max = {x: null, y: null};
+
+    for (var i = 0; i < this.currentPath_.length; i++) {
+      var p = this.currentPath_[i];
+
+      if (p.type == "moveTo") {
+        lineStr.push(" m ");
+        var c = this.getCoords_(p.x, p.y);
+        lineStr.push(mr(c.x), ",", mr(c.y));
+      } else if (p.type == "lineTo") {
+        lineStr.push(" l ");
+        var c = this.getCoords_(p.x, p.y);
+        lineStr.push(mr(c.x), ",", mr(c.y));
+      } else if (p.type == "close") {
+        lineStr.push(" x ");
+      } else if (p.type == "bezierCurveTo") {
+        lineStr.push(" c ");
+        var c = this.getCoords_(p.x, p.y);
+        var c1 = this.getCoords_(p.cp1x, p.cp1y);
+        var c2 = this.getCoords_(p.cp2x, p.cp2y);
+        lineStr.push(mr(c1.x), ",", mr(c1.y), ",",
+                     mr(c2.x), ",", mr(c2.y), ",",
+                     mr(c.x), ",", mr(c.y));
+      } else if (p.type == "at" || p.type == "wa") {
+        lineStr.push(" ", p.type, " ");
+        var c  = this.getCoords_(p.x, p.y);
+        var cStart = this.getCoords_(p.xStart, p.yStart);
+        var cEnd = this.getCoords_(p.xEnd, p.yEnd);
+
+        lineStr.push(mr(c.x - this.arcScaleX_ * p.radius), ",",
+                     mr(c.y - this.arcScaleY_ * p.radius), " ",
+                     mr(c.x + this.arcScaleX_ * p.radius), ",",
+                     mr(c.y + this.arcScaleY_ * p.radius), " ",
+                     mr(cStart.x), ",", mr(cStart.y), " ",
+                     mr(cEnd.x), ",", mr(cEnd.y));
+      }
+
+
+      // TODO: Following is broken for curves due to
+      //       move to proper paths.
+
+      // Figure out dimensions so we can do gradient fills
+      // properly
+      if(c) {
+        if (min.x == null || c.x < min.x) {
+          min.x = c.x;
+        }
+        if (max.x == null || c.x > max.x) {
+          max.x = c.x;
+        }
+        if (min.y == null || c.y < min.y) {
+          min.y = c.y;
+        }
+        if (max.y == null || c.y > max.y) {
+          max.y = c.y;
+        }
+      }
+    }
+    lineStr.push(' ">');
+
+    if (typeof this.fillStyle == "object") {
+      var focus = {x: "50%", y: "50%"};
+      var width = (max.x - min.x);
+      var height = (max.y - min.y);
+      var dimension = (width > height) ? width : height;
+
+      focus.x = mr((this.fillStyle.focus_.x / width) * 100 + 50) + "%";
+      focus.y = mr((this.fillStyle.focus_.y / height) * 100 + 50) + "%";
+
+      var colors = [];
+
+      // inside radius (%)
+      if (this.fillStyle.type_ == "gradientradial") {
+        var inside = (this.fillStyle.radius1_ / dimension * 100);
+
+        // percentage that outside radius exceeds inside radius
+        var expansion = (this.fillStyle.radius2_ / dimension * 100) - inside;
+      } else {
+        var inside = 0;
+        var expansion = 100;
+      }
+
+      var insidecolor = {offset: null, color: null};
+      var outsidecolor = {offset: null, color: null};
+
+      // We need to sort 'colors' by percentage, from 0 > 100 otherwise ie
+      // won't interpret it correctly
+      this.fillStyle.colors_.sort(function (cs1, cs2) {
+        return cs1.offset - cs2.offset;
+      });
+
+      for (var i = 0; i < this.fillStyle.colors_.length; i++) {
+        var fs = this.fillStyle.colors_[i];
+
+        colors.push( (fs.offset * expansion) + inside, "% ", fs.color, ",");
+
+        if (fs.offset > insidecolor.offset || insidecolor.offset == null) {
+          insidecolor.offset = fs.offset;
+          insidecolor.color = fs.color;
+        }
+
+        if (fs.offset < outsidecolor.offset || outsidecolor.offset == null) {
+          outsidecolor.offset = fs.offset;
+          outsidecolor.color = fs.color;
+        }
+      }
+      colors.pop();
+
+      lineStr.push('<g_vml_:fill',
+                   ' color="', outsidecolor.color, '"',
+                   ' color2="', insidecolor.color, '"',
+                   ' type="', this.fillStyle.type_, '"',
+                   ' focusposition="', focus.x, ', ', focus.y, '"',
+                   ' colors="', colors.join(""), '"',
+                   ' opacity="', opacity, '" />');
+    } else if (aFill) {
+      lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity, '" />');
+    } else {
+      lineStr.push(
+        '<g_vml_:stroke',
+        ' opacity="', opacity,'"',
+        ' joinstyle="', this.lineJoin, '"',
+        ' miterlimit="', this.miterLimit, '"',
+        ' endcap="', processLineCap(this.lineCap) ,'"',
+        ' weight="', this.lineWidth, 'px"',
+        ' color="', color,'" />'
+      );
+    }
+
+    lineStr.push("</g_vml_:shape>");
+
+    this.element_.insertAdjacentHTML("beforeEnd", lineStr.join(""));
+
+    //this.currentPath_ = [];
+  };
+
+  contextPrototype.fill = function() {
+    this.stroke(true);
+  };
+
+  contextPrototype.closePath = function() {
+    this.currentPath_.push({type: "close"});
+  };
+
+  /**
+   * @private
+   */
+  contextPrototype.getCoords_ = function(aX, aY) {
+    return {
+      x: Z * (aX * this.m_[0][0] + aY * this.m_[1][0] + this.m_[2][0]) - Z2,
+      y: Z * (aX * this.m_[0][1] + aY * this.m_[1][1] + this.m_[2][1]) - Z2
+    }
+  };
+
+  contextPrototype.save = function() {
+    var o = {};
+    copyState(this, o);
+    this.aStack_.push(o);
+    this.mStack_.push(this.m_);
+    this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
+  };
+
+  contextPrototype.restore = function() {
+    copyState(this.aStack_.pop(), this);
+    this.m_ = this.mStack_.pop();
+  };
+
+  contextPrototype.translate = function(aX, aY) {
+    var m1 = [
+      [1,  0,  0],
+      [0,  1,  0],
+      [aX, aY, 1]
+    ];
+
+    this.m_ = matrixMultiply(m1, this.m_);
+  };
+
+  contextPrototype.rotate = function(aRot) {
+    var c = mc(aRot);
+    var s = ms(aRot);
+
+    var m1 = [
+      [c,  s, 0],
+      [-s, c, 0],
+      [0,  0, 1]
+    ];
+
+    this.m_ = matrixMultiply(m1, this.m_);
+  };
+
+  contextPrototype.scale = function(aX, aY) {
+    this.arcScaleX_ *= aX;
+    this.arcScaleY_ *= aY;
+    var m1 = [
+      [aX, 0,  0],
+      [0,  aY, 0],
+      [0,  0,  1]
+    ];
+
+    this.m_ = matrixMultiply(m1, this.m_);
+  };
+
+  /******** STUBS ********/
+  contextPrototype.clip = function() {
+    // TODO: Implement
+  };
+
+  contextPrototype.arcTo = function() {
+    // TODO: Implement
+  };
+
+  contextPrototype.createPattern = function() {
+    return new CanvasPattern_;
+  };
+
+  // Gradient / Pattern Stubs
+  function CanvasGradient_(aType) {
+    this.type_ = aType;
+    this.radius1_ = 0;
+    this.radius2_ = 0;
+    this.colors_ = [];
+    this.focus_ = {x: 0, y: 0};
+  }
+
+  CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
+    aColor = processStyle(aColor);
+    this.colors_.push({offset: 1-aOffset, color: aColor});
+  };
+
+  function CanvasPattern_() {}
+
+  // set up externs
+  G_vmlCanvasManager = G_vmlCanvasManager_;
+  CanvasRenderingContext2D = CanvasRenderingContext2D_;
+  CanvasGradient = CanvasGradient_;
+  CanvasPattern = CanvasPattern_;
+
+})();
+
+} // if
Binary file web/data/favicon.ico has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/data/jquery.flot.js	Wed Jun 03 09:15:20 2009 +0200
@@ -0,0 +1,1 @@
+(function(F){function D(AO,e,f){var W=[],o={colors:["#edc240","#afd8f8","#cb4b4b","#4da74d","#9440ed"],legend:{show:true,noColumns:1,labelFormatter:null,labelBoxBorderColor:"#ccc",container:null,position:"ne",margin:5,backgroundColor:null,backgroundOpacity:0.85},xaxis:{mode:null,min:null,max:null,autoscaleMargin:null,ticks:null,tickFormatter:null,labelWidth:null,labelHeight:null,tickDecimals:null,tickSize:null,minTickSize:null,monthNames:null,timeformat:null},yaxis:{autoscaleMargin:0.02},x2axis:{autoscaleMargin:null},y2axis:{autoscaleMargin:0.02},points:{show:false,radius:3,lineWidth:2,fill:true,fillColor:"#ffffff"},lines:{show:false,lineWidth:2,fill:false,fillColor:null},bars:{show:false,lineWidth:2,barWidth:1,fill:true,fillColor:null,align:"left"},grid:{color:"#545454",backgroundColor:null,tickColor:"#dddddd",labelMargin:5,borderWidth:2,markings:null,markingsColor:"#f4f4f4",markingsLineWidth:2,clickable:false,hoverable:false,autoHighlight:true,mouseActiveRadius:10},selection:{mode:null,color:"#e8cfac"},shadowSize:4},X=null,AP=null,AQ=null,g=null,AX=null,K=AO,AA={xaxis:{},yaxis:{},x2axis:{},y2axis:{}},m={left:0,right:0,top:0,bottom:0},AI=0,Z=0,N=0,AB=0,S={};this.setData=n;this.setupGrid=s;this.draw=AU;this.clearSelection=I;this.setSelection=AC;this.getCanvas=function(){return X};this.getPlotOffset=function(){return m};this.getData=function(){return W};this.getAxes=function(){return AA};this.highlight=AS;this.unhighlight=AH;y(f);n(e);j();s();AU();function n(AY){W=U(AY);c();t()}function U(Ac){var Aa=[];for(var AZ=0;AZ<Ac.length;++AZ){var Ab;if(Ac[AZ].data){Ab={};for(var AY in Ac[AZ]){Ab[AY]=Ac[AZ][AY]}}else{Ab={data:Ac[AZ]}}Aa.push(Ab)}return Aa}function y(AY){F.extend(true,o,AY);if(o.xaxis.noTicks&&o.xaxis.ticks==null){o.xaxis.ticks=o.xaxis.noTicks}if(o.yaxis.noTicks&&o.yaxis.ticks==null){o.yaxis.ticks=o.yaxis.noTicks}if(o.grid.coloredAreas){o.grid.markings=o.grid.coloredAreas}if(o.grid.coloredAreasColor){o.grid.markingsColor=o.grid.coloredAreasColor}}function c(){var Ad;var Ai=W.length,AY=[],Ab=[];for(Ad=0;Ad<W.length;++Ad){var Ah=W[Ad].color;if(Ah!=null){--Ai;if(typeof Ah=="number"){Ab.push(Ah)}else{AY.push(E(W[Ad].color))}}}for(Ad=0;Ad<Ab.length;++Ad){Ai=Math.max(Ai,Ab[Ad]+1)}var AZ=[],Ac=0;Ad=0;while(AZ.length<Ai){var Ag;if(o.colors.length==Ad){Ag=new G(100,100,100)}else{Ag=E(o.colors[Ad])}var Aa=Ac%2==1?-1:1;var Af=1+Aa*Math.ceil(Ac/2)*0.2;Ag.scale(Af,Af,Af);AZ.push(Ag);++Ad;if(Ad>=o.colors.length){Ad=0;++Ac}}var Ae=0,Aj;for(Ad=0;Ad<W.length;++Ad){Aj=W[Ad];if(Aj.color==null){Aj.color=AZ[Ae].toString();++Ae}else{if(typeof Aj.color=="number"){Aj.color=AZ[Aj.color].toString()}}Aj.lines=F.extend(true,{},o.lines,Aj.lines);Aj.points=F.extend(true,{},o.points,Aj.points);Aj.bars=F.extend(true,{},o.bars,Aj.bars);if(Aj.shadowSize==null){Aj.shadowSize=o.shadowSize}if(Aj.xaxis&&Aj.xaxis==2){Aj.xaxis=AA.x2axis}else{Aj.xaxis=AA.xaxis}if(Aj.yaxis&&Aj.yaxis==2){Aj.yaxis=AA.y2axis}else{Aj.yaxis=AA.yaxis}}}function t(){var Aa=Number.POSITIVE_INFINITY,AZ=Number.NEGATIVE_INFINITY,Ab;for(Ab in AA){AA[Ab].datamin=Aa;AA[Ab].datamax=AZ;AA[Ab].used=false}for(var Ae=0;Ae<W.length;++Ae){var Ad=W[Ae].data,Aj=W[Ae].xaxis,Ai=W[Ae].yaxis,AY=0,Ah=0;if(W[Ae].bars.show){AY=W[Ae].bars.align=="left"?0:-W[Ae].bars.barWidth/2;Ah=AY+W[Ae].bars.barWidth}Aj.used=Ai.used=true;for(var Ac=0;Ac<Ad.length;++Ac){if(Ad[Ac]==null){continue}var Ag=Ad[Ac][0],Af=Ad[Ac][1];if(Ag!=null&&!isNaN(Ag=+Ag)){if(Ag+AY<Aj.datamin){Aj.datamin=Ag+AY}if(Ag+Ah>Aj.datamax){Aj.datamax=Ag+Ah}}if(Af!=null&&!isNaN(Af=+Af)){if(Af<Ai.datamin){Ai.datamin=Af}if(Af>Ai.datamax){Ai.datamax=Af}}if(Ag==null||Af==null||isNaN(Ag)||isNaN(Af)){Ad[Ac]=null}}}for(Ab in AA){if(AA[Ab].datamin==Aa){AA[Ab].datamin=0}if(AA[Ab].datamax==AZ){AA[Ab].datamax=1}}}function j(){AI=K.width();Z=K.height();K.html("");K.css("position","relative");if(AI<=0||Z<=0){throw"Invalid dimensions for plot, width = "+AI+", height = "+Z}X=F('<canvas width="'+AI+'" height="'+Z+'"></canvas>').appendTo(K).get(0);if(F.browser.msie){X=window.G_vmlCanvasManager.initElement(X)}g=X.getContext("2d");AP=F('<canvas style="position:absolute;left:0px;top:0px;" width="'+AI+'" height="'+Z+'"></canvas>').appendTo(K).get(0);if(F.browser.msie){AP=window.G_vmlCanvasManager.initElement(AP)}AX=AP.getContext("2d");AQ=F([AP,X]);if(o.selection.mode!=null||o.grid.hoverable){AQ.each(function(){this.onmousemove=J});if(o.selection.mode!=null){AQ.mousedown(AN)}}if(o.grid.clickable){AQ.click(k)}}function s(){function AY(Ab,Aa){Q(Ab,Aa);L(Ab,Aa);w(Ab,Aa);if(Ab==AA.xaxis||Ab==AA.x2axis){Ab.p2c=function(Ac){return(Ac-Ab.min)*Ab.scale};Ab.c2p=function(Ac){return Ab.min+Ac/Ab.scale}}else{Ab.p2c=function(Ac){return(Ab.max-Ac)*Ab.scale};Ab.c2p=function(Ac){return Ab.max-Ac/Ab.scale}}}for(var AZ in AA){AY(AA[AZ],o[AZ])}AW();p();AV()}function Q(Ab,Ad){var Aa=Ad.min!=null?Ad.min:Ab.datamin;var AY=Ad.max!=null?Ad.max:Ab.datamax;if(AY-Aa==0){var AZ;if(AY==0){AZ=1}else{AZ=0.01}Aa-=AZ;AY+=AZ}else{var Ac=Ad.autoscaleMargin;if(Ac!=null){if(Ad.min==null){Aa-=(AY-Aa)*Ac;if(Aa<0&&Ab.datamin>=0){Aa=0}}if(Ad.max==null){AY+=(AY-Aa)*Ac;if(AY>0&&Ab.datamax<=0){AY=0}}}}Ab.min=Aa;Ab.max=AY}function L(Ad,Ag){var Ac;if(typeof Ag.ticks=="number"&&Ag.ticks>0){Ac=Ag.ticks}else{if(Ad==AA.xaxis||Ad==AA.x2axis){Ac=AI/100}else{Ac=Z/60}}var Al=(Ad.max-Ad.min)/Ac;var Ao,Ah,Aj,Ak,Af,Aa,AZ;if(Ag.mode=="time"){function An(Av,Ap,Ar){var Aq=function(Ax){Ax=""+Ax;return Ax.length==1?"0"+Ax:Ax};var Au=[];var At=false;if(Ar==null){Ar=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]}for(var As=0;As<Ap.length;++As){var Aw=Ap.charAt(As);if(At){switch(Aw){case"h":Aw=""+Av.getUTCHours();break;case"H":Aw=Aq(Av.getUTCHours());break;case"M":Aw=Aq(Av.getUTCMinutes());break;case"S":Aw=Aq(Av.getUTCSeconds());break;case"d":Aw=""+Av.getUTCDate();break;case"m":Aw=""+(Av.getUTCMonth()+1);break;case"y":Aw=""+Av.getUTCFullYear();break;case"b":Aw=""+Ar[Av.getUTCMonth()];break}Au.push(Aw);At=false}else{if(Aw=="%"){At=true}else{Au.push(Aw)}}}return Au.join("")}var Ai={second:1000,minute:60*1000,hour:60*60*1000,day:24*60*60*1000,month:30*24*60*60*1000,year:365.2425*24*60*60*1000};var Am=[[1,"second"],[2,"second"],[5,"second"],[10,"second"],[30,"second"],[1,"minute"],[2,"minute"],[5,"minute"],[10,"minute"],[30,"minute"],[1,"hour"],[2,"hour"],[4,"hour"],[8,"hour"],[12,"hour"],[1,"day"],[2,"day"],[3,"day"],[0.25,"month"],[0.5,"month"],[1,"month"],[2,"month"],[3,"month"],[6,"month"],[1,"year"]];var Ab=0;if(Ag.minTickSize!=null){if(typeof Ag.tickSize=="number"){Ab=Ag.tickSize}else{Ab=Ag.minTickSize[0]*Ai[Ag.minTickSize[1]]}}for(Af=0;Af<Am.length-1;++Af){if(Al<(Am[Af][0]*Ai[Am[Af][1]]+Am[Af+1][0]*Ai[Am[Af+1][1]])/2&&Am[Af][0]*Ai[Am[Af][1]]>=Ab){break}}Ao=Am[Af][0];Aj=Am[Af][1];if(Aj=="year"){Aa=Math.pow(10,Math.floor(Math.log(Al/Ai.year)/Math.LN10));AZ=(Al/Ai.year)/Aa;if(AZ<1.5){Ao=1}else{if(AZ<3){Ao=2}else{if(AZ<7.5){Ao=5}else{Ao=10}}}Ao*=Aa}if(Ag.tickSize){Ao=Ag.tickSize[0];Aj=Ag.tickSize[1]}Ah=function(Ar){var Aw=[],Au=Ar.tickSize[0],Ax=Ar.tickSize[1],Av=new Date(Ar.min);var Aq=Au*Ai[Ax];if(Ax=="second"){Av.setUTCSeconds(C(Av.getUTCSeconds(),Au))}if(Ax=="minute"){Av.setUTCMinutes(C(Av.getUTCMinutes(),Au))}if(Ax=="hour"){Av.setUTCHours(C(Av.getUTCHours(),Au))}if(Ax=="month"){Av.setUTCMonth(C(Av.getUTCMonth(),Au))}if(Ax=="year"){Av.setUTCFullYear(C(Av.getUTCFullYear(),Au))}Av.setUTCMilliseconds(0);if(Aq>=Ai.minute){Av.setUTCSeconds(0)}if(Aq>=Ai.hour){Av.setUTCMinutes(0)}if(Aq>=Ai.day){Av.setUTCHours(0)}if(Aq>=Ai.day*4){Av.setUTCDate(1)}if(Aq>=Ai.year){Av.setUTCMonth(0)}var Az=0,Ay=Number.NaN,As;do{As=Ay;Ay=Av.getTime();Aw.push({v:Ay,label:Ar.tickFormatter(Ay,Ar)});if(Ax=="month"){if(Au<1){Av.setUTCDate(1);var Ap=Av.getTime();Av.setUTCMonth(Av.getUTCMonth()+1);var At=Av.getTime();Av.setTime(Ay+Az*Ai.hour+(At-Ap)*Au);Az=Av.getUTCHours();Av.setUTCHours(0)}else{Av.setUTCMonth(Av.getUTCMonth()+Au)}}else{if(Ax=="year"){Av.setUTCFullYear(Av.getUTCFullYear()+Au)}else{Av.setTime(Ay+Aq)}}}while(Ay<Ar.max&&Ay!=As);return Aw};Ak=function(Ap,As){var At=new Date(Ap);if(Ag.timeformat!=null){return An(At,Ag.timeformat,Ag.monthNames)}var Aq=As.tickSize[0]*Ai[As.tickSize[1]];var Ar=As.max-As.min;if(Aq<Ai.minute){fmt="%h:%M:%S"}else{if(Aq<Ai.day){if(Ar<2*Ai.day){fmt="%h:%M"}else{fmt="%b %d %h:%M"}}else{if(Aq<Ai.month){fmt="%b %d"}else{if(Aq<Ai.year){if(Ar<Ai.year){fmt="%b"}else{fmt="%b %y"}}else{fmt="%y"}}}}return An(At,fmt,Ag.monthNames)}}else{var AY=Ag.tickDecimals;var Ae=-Math.floor(Math.log(Al)/Math.LN10);if(AY!=null&&Ae>AY){Ae=AY}Aa=Math.pow(10,-Ae);AZ=Al/Aa;if(AZ<1.5){Ao=1}else{if(AZ<3){Ao=2;if(AZ>2.25&&(AY==null||Ae+1<=AY)){Ao=2.5;++Ae}}else{if(AZ<7.5){Ao=5}else{Ao=10}}}Ao*=Aa;if(Ag.minTickSize!=null&&Ao<Ag.minTickSize){Ao=Ag.minTickSize}if(Ag.tickSize!=null){Ao=Ag.tickSize}Ad.tickDecimals=Math.max(0,(AY!=null)?AY:Ae);Ah=function(Ar){var At=[];var Au=C(Ar.min,Ar.tickSize),Aq=0,Ap=Number.NaN,As;do{As=Ap;Ap=Au+Aq*Ar.tickSize;At.push({v:Ap,label:Ar.tickFormatter(Ap,Ar)});++Aq}while(Ap<Ar.max&&Ap!=As);return At};Ak=function(Ap,Aq){return Ap.toFixed(Aq.tickDecimals)}}Ad.tickSize=Aj?[Ao,Aj]:Ao;Ad.tickGenerator=Ah;if(F.isFunction(Ag.tickFormatter)){Ad.tickFormatter=function(Ap,Aq){return""+Ag.tickFormatter(Ap,Aq)}}else{Ad.tickFormatter=Ak}if(Ag.labelWidth!=null){Ad.labelWidth=Ag.labelWidth}if(Ag.labelHeight!=null){Ad.labelHeight=Ag.labelHeight}}function w(Ac,Ae){Ac.ticks=[];if(!Ac.used){return }if(Ae.ticks==null){Ac.ticks=Ac.tickGenerator(Ac)}else{if(typeof Ae.ticks=="number"){if(Ae.ticks>0){Ac.ticks=Ac.tickGenerator(Ac)}}else{if(Ae.ticks){var Ad=Ae.ticks;if(F.isFunction(Ad)){Ad=Ad({min:Ac.min,max:Ac.max})}var Ab,AY;for(Ab=0;Ab<Ad.length;++Ab){var AZ=null;var Aa=Ad[Ab];if(typeof Aa=="object"){AY=Aa[0];if(Aa.length>1){AZ=Aa[1]}}else{AY=Aa}if(AZ==null){AZ=Ac.tickFormatter(AY,Ac)}Ac.ticks[Ab]={v:AY,label:AZ}}}}}if(Ae.autoscaleMargin!=null&&Ac.ticks.length>0){if(Ae.min==null){Ac.min=Math.min(Ac.min,Ac.ticks[0].v)}if(Ae.max==null&&Ac.ticks.length>1){Ac.max=Math.min(Ac.max,Ac.ticks[Ac.ticks.length-1].v)}}}function AW(){function AZ(Ac){if(Ac.labelWidth==null){Ac.labelWidth=AI/6}if(Ac.labelHeight==null){labels=[];for(i=0;i<Ac.ticks.length;++i){l=Ac.ticks[i].label;if(l){labels.push('<div class="tickLabel" style="float:left;width:'+Ac.labelWidth+'px">'+l+"</div>")}}Ac.labelHeight=0;if(labels.length>0){var Ab=F('<div style="position:absolute;top:-10000px;width:10000px;font-size:smaller">'+labels.join("")+'<div style="clear:left"></div></div>').appendTo(K);Ac.labelHeight=Ab.height();Ab.remove()}}}function AY(Ae){if(Ae.labelWidth==null||Ae.labelHeight==null){var Ad,Af=[],Ac;for(Ad=0;Ad<Ae.ticks.length;++Ad){Ac=Ae.ticks[Ad].label;if(Ac){Af.push('<div class="tickLabel">'+Ac+"</div>")}}if(Af.length>0){var Ab=F('<div style="position:absolute;top:-10000px;font-size:smaller">'+Af.join("")+"</div>").appendTo(K);if(Ae.labelWidth==null){Ae.labelWidth=Ab.width()}if(Ae.labelHeight==null){Ae.labelHeight=Ab.find("div").height()}Ab.remove()}if(Ae.labelWidth==null){Ae.labelWidth=0}if(Ae.labelHeight==null){Ae.labelHeight=0}}}AZ(AA.xaxis);AY(AA.yaxis);AZ(AA.x2axis);AY(AA.y2axis);var Aa=o.grid.borderWidth/2;for(i=0;i<W.length;++i){Aa=Math.max(Aa,2*(W[i].points.radius+W[i].points.lineWidth/2))}m.left=m.right=m.top=m.bottom=Aa;if(AA.xaxis.labelHeight>0){m.bottom=Math.max(Aa,AA.xaxis.labelHeight+o.grid.labelMargin)}if(AA.yaxis.labelWidth>0){m.left=Math.max(Aa,AA.yaxis.labelWidth+o.grid.labelMargin)}if(AA.x2axis.labelHeight>0){m.top=Math.max(Aa,AA.x2axis.labelHeight+o.grid.labelMargin)}if(AA.y2axis.labelWidth>0){m.right=Math.max(Aa,AA.y2axis.labelWidth+o.grid.labelMargin)}N=AI-m.left-m.right;AB=Z-m.bottom-m.top;AA.xaxis.scale=N/(AA.xaxis.max-AA.xaxis.min);AA.yaxis.scale=AB/(AA.yaxis.max-AA.yaxis.min);AA.x2axis.scale=N/(AA.x2axis.max-AA.x2axis.min);AA.y2axis.scale=AB/(AA.y2axis.max-AA.y2axis.min)}function AU(){a();for(var AY=0;AY<W.length;AY++){AK(W[AY])}}function V(AZ,Af){var Ac=Af+"axis",AY=Af+"2axis",Ab,Ae,Ad,Aa;if(AZ[Ac]){Ab=AA[Ac];Ae=AZ[Ac].from;Ad=AZ[Ac].to}else{if(AZ[AY]){Ab=AA[AY];Ae=AZ[AY].from;Ad=AZ[AY].to}else{Ab=AA[Ac];Ae=AZ[Af+"1"];Ad=AZ[Af+"2"]}}if(Ae!=null&&Ad!=null&&Ae>Ad){return{from:Ad,to:Ae,axis:Ab}}return{from:Ae,to:Ad,axis:Ab}}function a(){var Ac;g.save();g.clearRect(0,0,AI,Z);g.translate(m.left,m.top);if(o.grid.backgroundColor){g.fillStyle=o.grid.backgroundColor;g.fillRect(0,0,N,AB)}if(o.grid.markings){var AZ=o.grid.markings;if(F.isFunction(AZ)){AZ=AZ({xmin:AA.xaxis.min,xmax:AA.xaxis.max,ymin:AA.yaxis.min,ymax:AA.yaxis.max,xaxis:AA.xaxis,yaxis:AA.yaxis,x2axis:AA.x2axis,y2axis:AA.y2axis})}for(Ac=0;Ac<AZ.length;++Ac){var AY=AZ[Ac],Ae=V(AY,"x"),Ab=V(AY,"y");if(Ae.from==null){Ae.from=Ae.axis.min}if(Ae.to==null){Ae.to=Ae.axis.max}if(Ab.from==null){Ab.from=Ab.axis.min}if(Ab.to==null){Ab.to=Ab.axis.max}if(Ae.to<Ae.axis.min||Ae.from>Ae.axis.max||Ab.to<Ab.axis.min||Ab.from>Ab.axis.max){continue}Ae.from=Math.max(Ae.from,Ae.axis.min);Ae.to=Math.min(Ae.to,Ae.axis.max);Ab.from=Math.max(Ab.from,Ab.axis.min);Ab.to=Math.min(Ab.to,Ab.axis.max);if(Ae.from==Ae.to&&Ab.from==Ab.to){continue}Ae.from=Ae.axis.p2c(Ae.from);Ae.to=Ae.axis.p2c(Ae.to);Ab.from=Ab.axis.p2c(Ab.from);Ab.to=Ab.axis.p2c(Ab.to);if(Ae.from==Ae.to||Ab.from==Ab.to){g.strokeStyle=AY.color||o.grid.markingsColor;g.lineWidth=AY.lineWidth||o.grid.markingsLineWidth;g.moveTo(Math.floor(Ae.from),Math.floor(Ab.from));g.lineTo(Math.floor(Ae.to),Math.floor(Ab.to));g.stroke()}else{g.fillStyle=AY.color||o.grid.markingsColor;g.fillRect(Math.floor(Ae.from),Math.floor(Ab.to),Math.floor(Ae.to-Ae.from),Math.floor(Ab.from-Ab.to))}}}g.lineWidth=1;g.strokeStyle=o.grid.tickColor;g.beginPath();var Aa,Ad=AA.xaxis;for(Ac=0;Ac<Ad.ticks.length;++Ac){Aa=Ad.ticks[Ac].v;if(Aa<=Ad.min||Aa>=AA.xaxis.max){continue}g.moveTo(Math.floor(Ad.p2c(Aa))+g.lineWidth/2,0);g.lineTo(Math.floor(Ad.p2c(Aa))+g.lineWidth/2,AB)}Ad=AA.yaxis;for(Ac=0;Ac<Ad.ticks.length;++Ac){Aa=Ad.ticks[Ac].v;if(Aa<=Ad.min||Aa>=Ad.max){continue}g.moveTo(0,Math.floor(Ad.p2c(Aa))+g.lineWidth/2);g.lineTo(N,Math.floor(Ad.p2c(Aa))+g.lineWidth/2)}Ad=AA.x2axis;for(Ac=0;Ac<Ad.ticks.length;++Ac){Aa=Ad.ticks[Ac].v;if(Aa<=Ad.min||Aa>=Ad.max){continue}g.moveTo(Math.floor(Ad.p2c(Aa))+g.lineWidth/2,-5);g.lineTo(Math.floor(Ad.p2c(Aa))+g.lineWidth/2,5)}Ad=AA.y2axis;for(Ac=0;Ac<Ad.ticks.length;++Ac){Aa=Ad.ticks[Ac].v;if(Aa<=Ad.min||Aa>=Ad.max){continue}g.moveTo(N-5,Math.floor(Ad.p2c(Aa))+g.lineWidth/2);g.lineTo(N+5,Math.floor(Ad.p2c(Aa))+g.lineWidth/2)}g.stroke();if(o.grid.borderWidth){g.lineWidth=o.grid.borderWidth;g.strokeStyle=o.grid.color;g.lineJoin="round";g.strokeRect(0,0,N,AB)}g.restore()}function p(){K.find(".tickLabels").remove();var AY='<div class="tickLabels" style="font-size:smaller;color:'+o.grid.color+'">';function AZ(Ac,Ad){for(var Ab=0;Ab<Ac.ticks.length;++Ab){var Aa=Ac.ticks[Ab];if(!Aa.label||Aa.v<Ac.min||Aa.v>Ac.max){continue}AY+=Ad(Aa,Ac)}}AZ(AA.xaxis,function(Aa,Ab){return'<div style="position:absolute;top:'+(m.top+AB+o.grid.labelMargin)+"px;left:"+(m.left+Ab.p2c(Aa.v)-Ab.labelWidth/2)+"px;width:"+Ab.labelWidth+'px;text-align:center" class="tickLabel">'+Aa.label+"</div>"});AZ(AA.yaxis,function(Aa,Ab){return'<div style="position:absolute;top:'+(m.top+Ab.p2c(Aa.v)-Ab.labelHeight/2)+"px;right:"+(m.right+N+o.grid.labelMargin)+"px;width:"+Ab.labelWidth+'px;text-align:right" class="tickLabel">'+Aa.label+"</div>"});AZ(AA.x2axis,function(Aa,Ab){return'<div style="position:absolute;bottom:'+(m.bottom+AB+o.grid.labelMargin)+"px;left:"+(m.left+Ab.p2c(Aa.v)-Ab.labelWidth/2)+"px;width:"+Ab.labelWidth+'px;text-align:center" class="tickLabel">'+Aa.label+"</div>"});AZ(AA.y2axis,function(Aa,Ab){return'<div style="position:absolute;top:'+(m.top+Ab.p2c(Aa.v)-Ab.labelHeight/2)+"px;left:"+(m.left+N+o.grid.labelMargin)+"px;width:"+Ab.labelWidth+'px;text-align:left" class="tickLabel">'+Aa.label+"</div>"});AY+="</div>";K.append(AY)}function AK(AY){if(AY.lines.show||(!AY.bars.show&&!AY.points.show)){h(AY)}if(AY.bars.show){u(AY)}if(AY.points.show){v(AY)}}function h(Aa){function AZ(Aj,Ah,An,Am){var Ag,Ao=null,Ad=null,Ap=null;g.beginPath();for(var Ai=0;Ai<Aj.length;++Ai){Ag=Ao;Ao=Aj[Ai];if(Ag==null||Ao==null){continue}var Af=Ag[0],Al=Ag[1],Ae=Ao[0],Ak=Ao[1];if(Al<=Ak&&Al<Am.min){if(Ak<Am.min){continue}Af=(Am.min-Al)/(Ak-Al)*(Ae-Af)+Af;Al=Am.min}else{if(Ak<=Al&&Ak<Am.min){if(Al<Am.min){continue}Ae=(Am.min-Al)/(Ak-Al)*(Ae-Af)+Af;Ak=Am.min}}if(Al>=Ak&&Al>Am.max){if(Ak>Am.max){continue}Af=(Am.max-Al)/(Ak-Al)*(Ae-Af)+Af;Al=Am.max}else{if(Ak>=Al&&Ak>Am.max){if(Al>Am.max){continue}Ae=(Am.max-Al)/(Ak-Al)*(Ae-Af)+Af;Ak=Am.max}}if(Af<=Ae&&Af<An.min){if(Ae<An.min){continue}Al=(An.min-Af)/(Ae-Af)*(Ak-Al)+Al;Af=An.min}else{if(Ae<=Af&&Ae<An.min){if(Af<An.min){continue}Ak=(An.min-Af)/(Ae-Af)*(Ak-Al)+Al;Ae=An.min}}if(Af>=Ae&&Af>An.max){if(Ae>An.max){continue}Al=(An.max-Af)/(Ae-Af)*(Ak-Al)+Al;Af=An.max}else{if(Ae>=Af&&Ae>An.max){if(Af>An.max){continue}Ak=(An.max-Af)/(Ae-Af)*(Ak-Al)+Al;Ae=An.max}}if(Ad!=An.p2c(Af)||Ap!=Am.p2c(Al)+Ah){g.moveTo(An.p2c(Af),Am.p2c(Al)+Ah)}Ad=An.p2c(Ae);Ap=Am.p2c(Ak)+Ah;g.lineTo(Ad,Ap)}g.stroke()}function Ab(Aj,Aq,Ao){var Ah,Ar=null;var Ad=Math.min(Math.max(0,Ao.min),Ao.max);var Am,Ag=0;var Ap=false;for(var Ai=0;Ai<Aj.length;++Ai){Ah=Ar;Ar=Aj[Ai];if(Ap&&Ah!=null&&Ar==null){g.lineTo(Aq.p2c(Ag),Ao.p2c(Ad));g.fill();Ap=false;continue}if(Ah==null||Ar==null){continue}var Af=Ah[0],An=Ah[1],Ae=Ar[0],Al=Ar[1];if(Af<=Ae&&Af<Aq.min){if(Ae<Aq.min){continue}An=(Aq.min-Af)/(Ae-Af)*(Al-An)+An;Af=Aq.min}else{if(Ae<=Af&&Ae<Aq.min){if(Af<Aq.min){continue}Al=(Aq.min-Af)/(Ae-Af)*(Al-An)+An;Ae=Aq.min}}if(Af>=Ae&&Af>Aq.max){if(Ae>Aq.max){continue}An=(Aq.max-Af)/(Ae-Af)*(Al-An)+An;Af=Aq.max}else{if(Ae>=Af&&Ae>Aq.max){if(Af>Aq.max){continue}Al=(Aq.max-Af)/(Ae-Af)*(Al-An)+An;Ae=Aq.max}}if(!Ap){g.beginPath();g.moveTo(Aq.p2c(Af),Ao.p2c(Ad));Ap=true}if(An>=Ao.max&&Al>=Ao.max){g.lineTo(Aq.p2c(Af),Ao.p2c(Ao.max));g.lineTo(Aq.p2c(Ae),Ao.p2c(Ao.max));continue}else{if(An<=Ao.min&&Al<=Ao.min){g.lineTo(Aq.p2c(Af),Ao.p2c(Ao.min));g.lineTo(Aq.p2c(Ae),Ao.p2c(Ao.min));continue}}var As=Af,Ak=Ae;if(An<=Al&&An<Ao.min&&Al>=Ao.min){Af=(Ao.min-An)/(Al-An)*(Ae-Af)+Af;An=Ao.min}else{if(Al<=An&&Al<Ao.min&&An>=Ao.min){Ae=(Ao.min-An)/(Al-An)*(Ae-Af)+Af;Al=Ao.min}}if(An>=Al&&An>Ao.max&&Al<=Ao.max){Af=(Ao.max-An)/(Al-An)*(Ae-Af)+Af;An=Ao.max}else{if(Al>=An&&Al>Ao.max&&An<=Ao.max){Ae=(Ao.max-An)/(Al-An)*(Ae-Af)+Af;Al=Ao.max}}if(Af!=As){if(An<=Ao.min){Am=Ao.min}else{Am=Ao.max}g.lineTo(Aq.p2c(As),Ao.p2c(Am));g.lineTo(Aq.p2c(Af),Ao.p2c(Am))}g.lineTo(Aq.p2c(Af),Ao.p2c(An));g.lineTo(Aq.p2c(Ae),Ao.p2c(Al));if(Ae!=Ak){if(Al<=Ao.min){Am=Ao.min}else{Am=Ao.max}g.lineTo(Aq.p2c(Ak),Ao.p2c(Am));g.lineTo(Aq.p2c(Ae),Ao.p2c(Am))}Ag=Math.max(Ae,Ak)}if(Ap){g.lineTo(Aq.p2c(Ag),Ao.p2c(Ad));g.fill()}}g.save();g.translate(m.left,m.top);g.lineJoin="round";var Ac=Aa.lines.lineWidth;var AY=Aa.shadowSize;if(AY>0){g.lineWidth=AY/2;g.strokeStyle="rgba(0,0,0,0.1)";AZ(Aa.data,Ac/2+AY/2+g.lineWidth/2,Aa.xaxis,Aa.yaxis);g.lineWidth=AY/2;g.strokeStyle="rgba(0,0,0,0.2)";AZ(Aa.data,Ac/2+g.lineWidth/2,Aa.xaxis,Aa.yaxis)}g.lineWidth=Ac;g.strokeStyle=Aa.color;AD(Aa.lines,Aa.color);if(Aa.lines.fill){Ab(Aa.data,Aa.xaxis,Aa.yaxis)}AZ(Aa.data,0,Aa.xaxis,Aa.yaxis);g.restore()}function v(AZ){function Ac(Ag,Ae,Ah,Ak,Ai){for(var Af=0;Af<Ag.length;++Af){if(Ag[Af]==null){continue}var Ad=Ag[Af][0],Aj=Ag[Af][1];if(Ad<Ak.min||Ad>Ak.max||Aj<Ai.min||Aj>Ai.max){continue}g.beginPath();g.arc(Ak.p2c(Ad),Ai.p2c(Aj),Ae,0,2*Math.PI,true);if(Ah){g.fill()}g.stroke()}}function Ab(Ag,Ai,Ae,Ak,Ah){for(var Af=0;Af<Ag.length;++Af){if(Ag[Af]==null){continue}var Ad=Ag[Af][0],Aj=Ag[Af][1];if(Ad<Ak.min||Ad>Ak.max||Aj<Ah.min||Aj>Ah.max){continue}g.beginPath();g.arc(Ak.p2c(Ad),Ah.p2c(Aj)+Ai,Ae,0,Math.PI,false);g.stroke()}}g.save();g.translate(m.left,m.top);var Aa=AZ.lines.lineWidth;var AY=AZ.shadowSize;if(AY>0){g.lineWidth=AY/2;g.strokeStyle="rgba(0,0,0,0.1)";Ab(AZ.data,AY/2+g.lineWidth/2,AZ.points.radius,AZ.xaxis,AZ.yaxis);g.lineWidth=AY/2;g.strokeStyle="rgba(0,0,0,0.2)";Ab(AZ.data,g.lineWidth/2,AZ.points.radius,AZ.xaxis,AZ.yaxis)}g.lineWidth=AZ.points.lineWidth;g.strokeStyle=AZ.color;AD(AZ.points,AZ.color);Ac(AZ.data,AZ.points.radius,AZ.points.fill,AZ.xaxis,AZ.yaxis);g.restore()}function AM(Aj,Ah,Ac,Ai,Aa,Ao,An,Ak,Af){var Am=true,Ae=true,Ab=true,Ad=false,AZ=Aj+Ac,Al=Aj+Ai,AY=0,Ag=Ah;if(Ag<AY){Ag=0;AY=Ah;Ad=true;Ab=false}if(Al<An.min||AZ>An.max||Ag<Ak.min||AY>Ak.max){return }if(AZ<An.min){AZ=An.min;Am=false}if(Al>An.max){Al=An.max;Ae=false}if(AY<Ak.min){AY=Ak.min;Ad=false}if(Ag>Ak.max){Ag=Ak.max;Ab=false}if(Ao){Af.beginPath();Af.moveTo(An.p2c(AZ),Ak.p2c(AY)+Aa);Af.lineTo(An.p2c(AZ),Ak.p2c(Ag)+Aa);Af.lineTo(An.p2c(Al),Ak.p2c(Ag)+Aa);Af.lineTo(An.p2c(Al),Ak.p2c(AY)+Aa);Af.fill()}if(Am||Ae||Ab||Ad){Af.beginPath();AZ=An.p2c(AZ);AY=Ak.p2c(AY);Al=An.p2c(Al);Ag=Ak.p2c(Ag);Af.moveTo(AZ,AY+Aa);if(Am){Af.lineTo(AZ,Ag+Aa)}else{Af.moveTo(AZ,Ag+Aa)}if(Ab){Af.lineTo(Al,Ag+Aa)}else{Af.moveTo(Al,Ag+Aa)}if(Ae){Af.lineTo(Al,AY+Aa)}else{Af.moveTo(Al,AY+Aa)}if(Ad){Af.lineTo(AZ,AY+Aa)}else{Af.moveTo(AZ,AY+Aa)}Af.stroke()}}function u(Aa){function AZ(Ae,Ab,Ad,Ah,Af,Ai,Ag){for(var Ac=0;Ac<Ae.length;Ac++){if(Ae[Ac]==null){continue}AM(Ae[Ac][0],Ae[Ac][1],Ab,Ad,Ah,Af,Ai,Ag,g)}}g.save();g.translate(m.left,m.top);g.lineJoin="round";g.lineWidth=Aa.bars.lineWidth;g.strokeStyle=Aa.color;AD(Aa.bars,Aa.color);var AY=Aa.bars.align=="left"?0:-Aa.bars.barWidth/2;AZ(Aa.data,AY,AY+Aa.bars.barWidth,0,Aa.bars.fill,Aa.xaxis,Aa.yaxis);g.restore()}function AD(Aa,AY){var AZ=Aa.fill;if(!AZ){return }if(Aa.fillColor){g.fillStyle=Aa.fillColor}else{var Ab=E(AY);Ab.a=typeof AZ=="number"?AZ:0.4;Ab.normalize();g.fillStyle=Ab.toString()}}function AV(){K.find(".legend").remove();if(!o.legend.show){return }var Ae=[];var Ac=false;for(i=0;i<W.length;++i){if(!W[i].label){continue}if(i%o.legend.noColumns==0){if(Ac){Ae.push("</tr>")}Ae.push("<tr>");Ac=true}var Ag=W[i].label;if(o.legend.labelFormatter!=null){Ag=o.legend.labelFormatter(Ag)}Ae.push('<td class="legendColorBox"><div style="border:1px solid '+o.legend.labelBoxBorderColor+';padding:1px"><div style="width:14px;height:10px;background-color:'+W[i].color+';overflow:hidden"></div></div></td><td class="legendLabel">'+Ag+"</td>")}if(Ac){Ae.push("</tr>")}if(Ae.length==0){return }var Ai='<table style="font-size:smaller;color:'+o.grid.color+'">'+Ae.join("")+"</table>";if(o.legend.container!=null){o.legend.container.html(Ai)}else{var Af="";var AZ=o.legend.position,Aa=o.legend.margin;if(AZ.charAt(0)=="n"){Af+="top:"+(Aa+m.top)+"px;"}else{if(AZ.charAt(0)=="s"){Af+="bottom:"+(Aa+m.bottom)+"px;"}}if(AZ.charAt(1)=="e"){Af+="right:"+(Aa+m.right)+"px;"}else{if(AZ.charAt(1)=="w"){Af+="left:"+(Aa+m.left)+"px;"}}var Ah=F('<div class="legend">'+Ai.replace('style="','style="position:absolute;'+Af+";")+"</div>").appendTo(K);if(o.legend.backgroundOpacity!=0){var Ad=o.legend.backgroundColor;if(Ad==null){var Ab;if(o.grid.backgroundColor){Ab=o.grid.backgroundColor}else{Ab=A(Ah)}Ad=E(Ab).adjust(null,null,null,1).toString()}var AY=Ah.children();F('<div style="position:absolute;width:'+AY.width()+"px;height:"+AY.height()+"px;"+Af+"background-color:"+Ad+';"> </div>').prependTo(Ah).css("opacity",o.legend.backgroundOpacity)}}}var AG={pageX:null,pageY:null},d={first:{x:-1,y:-1},second:{x:-1,y:-1},show:false,active:false},AF=[],P=false,O=null,z=null;function AT(Ae,Ac){var Al=o.grid.mouseActiveRadius,Ar=Al*Al+1,At=null,An=false;function Ai(Ay,Ax){return{datapoint:W[Ay].data[Ax],dataIndex:Ax,series:W[Ay],seriesIndex:Ay}}for(var Aq=0;Aq<W.length;++Aq){var Aw=W[Aq].data,Ad=W[Aq].xaxis,Ab=W[Aq].yaxis,Am=Ad.c2p(Ae),Ak=Ab.c2p(Ac),AZ=Al/Ad.scale,AY=Al/Ab.scale,Av=W[Aq].bars.show,Au=!(W[Aq].bars.show&&!(W[Aq].lines.show||W[Aq].points.show)),Aa=W[Aq].bars.align=="left"?0:-W[Aq].bars.barWidth/2,As=Aa+W[Aq].bars.barWidth;for(var Ap=0;Ap<Aw.length;++Ap){if(Aw[Ap]==null){continue}var Ag=Aw[Ap][0],Af=Aw[Ap][1];if(Av){if(!An&&Am>=Ag+Aa&&Am<=Ag+As&&Ak>=Math.min(0,Af)&&Ak<=Math.max(0,Af)){At=Ai(Aq,Ap)}}if(Au){if((Ag-Am>AZ||Ag-Am<-AZ)||(Af-Ak>AY||Af-Ak<-AY)){continue}var Aj=Math.abs(Ad.p2c(Ag)-Ae),Ah=Math.abs(Ab.p2c(Af)-Ac),Ao=Aj*Aj+Ah*Ah;if(Ao<Ar){Ar=Ao;An=true;At=Ai(Aq,Ap)}}}}return At}function J(AZ){var Aa=AZ||window.event;if(Aa.pageX==null&&Aa.clientX!=null){var Ab=document.documentElement,AY=document.body;AG.pageX=Aa.clientX+(Ab&&Ab.scrollLeft||AY.scrollLeft||0);AG.pageY=Aa.clientY+(Ab&&Ab.scrollTop||AY.scrollTop||0)}else{AG.pageX=Aa.pageX;AG.pageY=Aa.pageY}if(o.grid.hoverable&&!z){z=setTimeout(R,100)}if(d.active){AL(AG)}}function AN(AY){if(AY.which!=1){return }document.body.focus();if(document.onselectstart!==undefined&&S.onselectstart==null){S.onselectstart=document.onselectstart;document.onselectstart=function(){return false}}if(document.ondrag!==undefined&&S.ondrag==null){S.ondrag=document.ondrag;document.ondrag=function(){return false}}AR(d.first,AY);AG.pageX=null;d.active=true;F(document).one("mouseup",Y)}function k(AY){if(P){P=false;return }M("plotclick",AY)}function R(){M("plothover",AG);z=null}function M(AZ,AY){var Aa=AQ.offset(),Af={pageX:AY.pageX,pageY:AY.pageY},Ad=AY.pageX-Aa.left-m.left,Ab=AY.pageY-Aa.top-m.top;if(AA.xaxis.used){Af.x=AA.xaxis.c2p(Ad)}if(AA.yaxis.used){Af.y=AA.yaxis.c2p(Ab)}if(AA.x2axis.used){Af.x2=AA.x2axis.c2p(Ad)}if(AA.y2axis.used){Af.y2=AA.y2axis.c2p(Ab)}var Ag=AT(Ad,Ab);if(Ag){Ag.pageX=parseInt(Ag.series.xaxis.p2c(Ag.datapoint[0])+Aa.left+m.left);Ag.pageY=parseInt(Ag.series.yaxis.p2c(Ag.datapoint[1])+Aa.top+m.top)}if(o.grid.autoHighlight){for(var Ac=0;Ac<AF.length;++Ac){var Ae=AF[Ac];if(Ae.auto&&!(Ag&&Ae.series==Ag.series&&Ae.point==Ag.datapoint)){AH(Ae.series,Ae.point)}}if(Ag){AS(Ag.series,Ag.datapoint,true)}}K.trigger(AZ,[Af,Ag])}function x(){if(!O){O=setTimeout(T,50)}}function T(){O=null;AX.save();AX.clearRect(0,0,AI,Z);AX.translate(m.left,m.top);var Ab,Aa;for(Ab=0;Ab<AF.length;++Ab){Aa=AF[Ab];if(Aa.series.bars.show){AJ(Aa.series,Aa.point)}else{AE(Aa.series,Aa.point)}}AX.restore();if(d.show&&b()){AX.strokeStyle=E(o.selection.color).scale(null,null,null,0.8).toString();AX.lineWidth=1;g.lineJoin="round";AX.fillStyle=E(o.selection.color).scale(null,null,null,0.4).toString();var AY=Math.min(d.first.x,d.second.x),Ad=Math.min(d.first.y,d.second.y),AZ=Math.abs(d.second.x-d.first.x),Ac=Math.abs(d.second.y-d.first.y);AX.fillRect(AY+m.left,Ad+m.top,AZ,Ac);AX.strokeRect(AY+m.left,Ad+m.top,AZ,Ac)}}function AS(Aa,AY,Ab){if(typeof Aa=="number"){Aa=W[Aa]}if(typeof AY=="number"){AY=Aa.data[AY]}var AZ=q(Aa,AY);if(AZ==-1){AF.push({series:Aa,point:AY,auto:Ab});x()}else{if(!Ab){AF[AZ].auto=false}}}function AH(Aa,AY){if(typeof Aa=="number"){Aa=W[Aa]}if(typeof AY=="number"){AY=Aa.data[AY]}var AZ=q(Aa,AY);if(AZ!=-1){AF.splice(AZ,1);x()}}function q(Aa,Ab){for(var AY=0;AY<AF.length;++AY){var AZ=AF[AY];if(AZ.series==Aa&&AZ.point[0]==Ab[0]&&AZ.point[1]==Ab[1]){return AY}}return -1}function AE(Ab,Aa){var AZ=Aa[0],Af=Aa[1],Ae=Ab.xaxis,Ad=Ab.yaxis;if(AZ<Ae.min||AZ>Ae.max||Af<Ad.min||Af>Ad.max){return }var Ac=Ab.points.radius+Ab.points.lineWidth/2;AX.lineWidth=Ac;AX.strokeStyle=E(Ab.color).scale(1,1,1,0.5).toString();var AY=1.5*Ac;AX.beginPath();AX.arc(Ae.p2c(AZ),Ad.p2c(Af),AY,0,2*Math.PI,true);AX.stroke()}function AJ(Aa,AY){AX.lineJoin="round";AX.lineWidth=Aa.bars.lineWidth;AX.strokeStyle=E(Aa.color).scale(1,1,1,0.5).toString();AX.fillStyle=E(Aa.color).scale(1,1,1,0.5).toString();var AZ=Aa.bars.align=="left"?0:-Aa.bars.barWidth/2;AM(AY[0],AY[1],AZ,AZ+Aa.bars.barWidth,0,true,Aa.xaxis,Aa.yaxis,AX)}function r(){var AZ=Math.min(d.first.x,d.second.x),AY=Math.max(d.first.x,d.second.x),Ab=Math.max(d.first.y,d.second.y),Aa=Math.min(d.first.y,d.second.y);var Ac={};if(AA.xaxis.used){Ac.xaxis={from:AA.xaxis.c2p(AZ),to:AA.xaxis.c2p(AY)}}if(AA.x2axis.used){Ac.x2axis={from:AA.x2axis.c2p(AZ),to:AA.x2axis.c2p(AY)}}if(AA.yaxis.used){Ac.yaxis={from:AA.yaxis.c2p(Ab),to:AA.yaxis.c2p(Aa)}}if(AA.y2axis.used){Ac.yaxis={from:AA.y2axis.c2p(Ab),to:AA.y2axis.c2p(Aa)}}K.trigger("plotselected",[Ac]);if(AA.xaxis.used&&AA.yaxis.used){K.trigger("selected",[{x1:Ac.xaxis.from,y1:Ac.yaxis.from,x2:Ac.xaxis.to,y2:Ac.yaxis.to}])}}function Y(AY){if(document.onselectstart!==undefined){document.onselectstart=S.onselectstart}if(document.ondrag!==undefined){document.ondrag=S.ondrag}d.active=false;AL(AY);if(b()){r();P=true}return false}function AR(Aa,AY){var AZ=AQ.offset();if(o.selection.mode=="y"){if(Aa==d.first){Aa.x=0}else{Aa.x=N}}else{Aa.x=AY.pageX-AZ.left-m.left;Aa.x=Math.min(Math.max(0,Aa.x),N)}if(o.selection.mode=="x"){if(Aa==d.first){Aa.y=0}else{Aa.y=AB}}else{Aa.y=AY.pageY-AZ.top-m.top;Aa.y=Math.min(Math.max(0,Aa.y),AB)}}function AL(AY){if(AY.pageX==null){return }AR(d.second,AY);if(b()){d.show=true;x()}else{I()}}function I(){if(d.show){d.show=false;x()}}function AC(AZ,AY){var Aa;if(o.selection.mode=="y"){d.first.x=0;d.second.x=N}else{Aa=V(AZ,"x");d.first.x=Aa.axis.p2c(Aa.from);d.second.x=Aa.axis.p2c(Aa.to)}if(o.selection.mode=="x"){d.first.y=0;d.second.y=AB}else{Aa=V(AZ,"y");d.first.y=Aa.axis.p2c(Aa.from);d.second.y=Aa.axis.p2c(Aa.to)}d.show=true;x();if(!AY){r()}}function b(){var AY=5;return Math.abs(d.second.x-d.first.x)>=AY&&Math.abs(d.second.y-d.first.y)>=AY}}F.plot=function(L,J,I){var K=new D(L,J,I);return K};function C(J,I){return I*Math.floor(J/I)}function H(J,K,I){if(K<J){return K}else{if(K>I){return I}else{return K}}}function G(O,N,J,L){var M=["r","g","b","a"];var I=4;while(-1<--I){this[M[I]]=arguments[I]||((I==3)?1:0)}this.toString=function(){if(this.a>=1){return"rgb("+[this.r,this.g,this.b].join(",")+")"}else{return"rgba("+[this.r,this.g,this.b,this.a].join(",")+")"}};this.scale=function(R,Q,S,P){I=4;while(-1<--I){if(arguments[I]!=null){this[M[I]]*=arguments[I]}}return this.normalize()};this.adjust=function(R,Q,S,P){I=4;while(-1<--I){if(arguments[I]!=null){this[M[I]]+=arguments[I]}}return this.normalize()};this.clone=function(){return new G(this.r,this.b,this.g,this.a)};var K=function(Q,P,R){return Math.max(Math.min(Q,R),P)};this.normalize=function(){this.r=K(parseInt(this.r),0,255);this.g=K(parseInt(this.g),0,255);this.b=K(parseInt(this.b),0,255);this.a=K(this.a,0,1);return this};this.normalize()}var B={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]};function A(J){var I,K=J;do{I=K.css("background-color").toLowerCase();if(I!=""&&I!="transparent"){break}K=K.parent()}while(!F.nodeName(K.get(0),"body"));if(I=="rgba(0, 0, 0, 0)"){return"transparent"}return I}function E(K){var I;if(I=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(K)){return new G(parseInt(I[1],10),parseInt(I[2],10),parseInt(I[3],10))}if(I=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(K)){return new G(parseInt(I[1],10),parseInt(I[2],10),parseInt(I[3],10),parseFloat(I[4]))}if(I=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(K)){return new G(parseFloat(I[1])*2.55,parseFloat(I[2])*2.55,parseFloat(I[3])*2.55)}if(I=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(K)){return new G(parseFloat(I[1])*2.55,parseFloat(I[2])*2.55,parseFloat(I[3])*2.55,parseFloat(I[4]))}if(I=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(K)){return new G(parseInt(I[1],16),parseInt(I[2],16),parseInt(I[3],16))}if(I=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(K)){return new G(parseInt(I[1]+I[1],16),parseInt(I[2]+I[2],16),parseInt(I[3]+I[3],16))}var J=F.trim(K).toLowerCase();if(J=="transparent"){return new G(255,255,255,0)}else{I=B[J];return new G(I[0],I[1],I[2])}}})(jQuery);
\ No newline at end of file
--- a/web/data/jquery.js	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/data/jquery.js	Wed Jun 03 09:15:20 2009 +0200
@@ -1,32 +1,19 @@
 /*
- * jQuery 1.2.6 - New Wave Javascript
+ * jQuery JavaScript Library v1.3.2
+ * http://jquery.com/
  *
- * Copyright (c) 2008 John Resig (jquery.com)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
+ * Copyright (c) 2009 John Resig
+ * Dual licensed under the MIT and GPL licenses.
+ * http://docs.jquery.com/License
  *
- * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $
- * $Rev: 5685 $
+ * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
+ * Revision: 6246
  */
-(function(){var _jQuery=window.jQuery,_$=window.$;var jQuery=window.jQuery=window.$=function(selector,context){return new jQuery.fn.init(selector,context);};var quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,isSimple=/^.[^:#\[\.]*$/,undefined;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;return this;}if(typeof selector=="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1])selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem){if(elem.id!=match[3])return jQuery().find(selector);return jQuery(elem);}selector=[];}}else
-return jQuery(context).find(selector);}else if(jQuery.isFunction(selector))return jQuery(document)[jQuery.fn.ready?"ready":"load"](selector);return this.setArray(jQuery.makeArray(selector));},jquery:"1.2.6",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(elems){var ret=jQuery(elems);ret.prevObject=this;return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){var ret=-1;return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this);},attr:function(name,value,type){var options=name;if(name.constructor==String)if(value===undefined)return this[0]&&jQuery[type||"attr"](this[0],name);else{options={};options[name]=value;}return this.each(function(i){for(name in options)jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0)value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!="object"&&text!=null)return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0])jQuery(html,this[0].ownerDocument).clone().insertBefore(this[0]).map(function(){var elem=this;while(elem.firstChild)elem=elem.firstChild;return elem;}).append(this);return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,false,function(elem){if(this.nodeType==1)this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,true,function(elem){if(this.nodeType==1)this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,true,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(selector){var elems=jQuery.map(this,function(elem){return jQuery.find(selector,elem);});return this.pushStack(/[^+>] [^+>]/.test(selector)||selector.indexOf("..")>-1?jQuery.unique(elems):elems);},clone:function(events){var ret=this.map(function(){if(jQuery.browser.msie&&!jQuery.isXMLDoc(this)){var clone=this.cloneNode(true),container=document.createElement("div");container.appendChild(clone);return jQuery.clean([container.innerHTML])[0];}else
-return this.cloneNode(true);});var clone=ret.find("*").andSelf().each(function(){if(this[expando]!=undefined)this[expando]=null;});if(events===true)this.find("*").andSelf().each(function(i){if(this.nodeType==3)return;var events=jQuery.data(this,"events");for(var type in events)for(var handler in events[type])jQuery.event.add(clone[i],type,events[type][handler],events[type][handler].data);});return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,this));},not:function(selector){if(selector.constructor==String)if(isSimple.test(selector))return this.pushStack(jQuery.multiFilter(selector,this,true));else
-selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector=='string'?jQuery(selector):jQuery.makeArray(selector))));},is:function(selector){return!!selector&&jQuery.multiFilter(selector,this).length>0;},hasClass:function(selector){return this.is("."+selector);},val:function(value){if(value==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0)return null;for(var i=one?index:0,max=one?index+1:options.length;i<max;i++){var option=options[i];if(option.selected){value=jQuery.browser.msie&&!option.attributes.value.specified?option.text:option.value;if(one)return value;values.push(value);}}return values;}else
-return(this[0].value||"").replace(/\r/g,"");}return undefined;}if(value.constructor==Number)value+='';return this.each(function(){if(this.nodeType!=1)return;if(value.constructor==Array&&/radio|checkbox/.test(this.type))this.checked=(jQuery.inArray(this.value,value)>=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length)this.selectedIndex=-1;}else
-this.value=value;});},html:function(value){return value==undefined?(this[0]?this[0].innerHTML:null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length)data=jQuery.data(this[0],key);return data===undefined&&parts[1]?this.data(parts[0]):data;}else
-return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},domManip:function(args,table,reverse,callback){var clone=this.length>1,elems;return this.each(function(){if(!elems){elems=jQuery.clean(args,this.ownerDocument);if(reverse)elems.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(elems[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(this.ownerDocument.createElement("tbody"));var scripts=jQuery([]);jQuery.each(elems,function(){var elem=clone?jQuery(this).clone(true)[0]:this;if(jQuery.nodeName(elem,"script"))scripts=scripts.add(elem);else{if(elem.nodeType==1)scripts=scripts.add(jQuery("script",elem).remove());callback.call(obj,elem);}});scripts.each(evalScript);});}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src)jQuery.ajax({url:elem.src,async:false,dataType:"script"});else
-jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode)elem.parentNode.removeChild(elem);}function now(){return+new Date;}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};i=2;}if(typeof target!="object"&&typeof target!="function")target={};if(length==i){target=this;--i;}for(;i<length;i++)if((options=arguments[i])!=null)for(var name in options){var src=target[name],copy=options[name];if(target===copy)continue;if(deep&&copy&&typeof copy=="object"&&!copy.nodeType)target[name]=jQuery.extend(deep,src||(copy.length!=null?[]:{}),copy);else if(copy!==undefined)target[name]=copy;}return target;};var expando="jQuery"+now(),uuid=0,windowData={},exclude=/z-?index|font-?weight|opacity|zoom|line-?height/i,defaultView=document.defaultView||{};jQuery.extend({noConflict:function(deep){window.$=_$;if(deep)window.jQuery=_jQuery;return jQuery;},isFunction:function(fn){return!!fn&&typeof fn!="string"&&!fn.nodeName&&fn.constructor!=Array&&/^[\s[]?function/.test(fn+"");},isXMLDoc:function(elem){return elem.documentElement&&!elem.body||elem.tagName&&elem.ownerDocument&&!elem.ownerDocument.body;},globalEval:function(data){data=jQuery.trim(data);if(data){var head=document.getElementsByTagName("head")[0]||document.documentElement,script=document.createElement("script");script.type="text/javascript";if(jQuery.browser.msie)script.text=data;else
-script.appendChild(document.createTextNode(data));head.insertBefore(script,head.firstChild);head.removeChild(script);}},nodeName:function(elem,name){return elem.nodeName&&elem.nodeName.toUpperCase()==name.toUpperCase();},cache:{},data:function(elem,name,data){elem=elem==window?windowData:elem;var id=elem[expando];if(!id)id=elem[expando]=++uuid;if(name&&!jQuery.cache[id])jQuery.cache[id]={};if(data!==undefined)jQuery.cache[id][name]=data;return name?jQuery.cache[id][name]:id;},removeData:function(elem,name){elem=elem==window?windowData:elem;var id=elem[expando];if(name){if(jQuery.cache[id]){delete jQuery.cache[id][name];name="";for(name in jQuery.cache[id])break;if(!name)jQuery.removeData(elem);}}else{try{delete elem[expando];}catch(e){if(elem.removeAttribute)elem.removeAttribute(expando);}delete jQuery.cache[id];}},each:function(object,callback,args){var name,i=0,length=object.length;if(args){if(length==undefined){for(name in object)if(callback.apply(object[name],args)===false)break;}else
-for(;i<length;)if(callback.apply(object[i++],args)===false)break;}else{if(length==undefined){for(name in object)if(callback.call(object[name],name,object[name])===false)break;}else
-for(var value=object[0];i<length&&callback.call(value,i,value)!==false;value=object[++i]){}}return object;},prop:function(elem,value,type,i,name){if(jQuery.isFunction(value))value=value.call(elem,i);return value&&value.constructor==Number&&type=="curCSS"&&!exclude.test(name)?value+"px":value;},className:{add:function(elem,classNames){jQuery.each((classNames||"").split(/\s+/),function(i,className){if(elem.nodeType==1&&!jQuery.className.has(elem.className,className))elem.className+=(elem.className?" ":"")+className;});},remove:function(elem,classNames){if(elem.nodeType==1)elem.className=classNames!=undefined?jQuery.grep(elem.className.split(/\s+/),function(className){return!jQuery.className.has(classNames,className);}).join(" "):"";},has:function(elem,className){return jQuery.inArray(className,(elem.className||elem).toString().split(/\s+/))>-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];}callback.call(elem);for(var name in options)elem.style[name]=old[name];},css:function(elem,name,force){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;var padding=0,border=0;jQuery.each(which,function(){padding+=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;border+=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});val-=Math.round(padding+border);}if(jQuery(elem).is(":visible"))getWH();else
-jQuery.swap(elem,props,getWH);return Math.max(0,val);}return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret,style=elem.style;function color(elem){if(!jQuery.browser.safari)return false;var ret=defaultView.getComputedStyle(elem,null);return!ret||ret.getPropertyValue("color")=="";}if(name=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret;}if(jQuery.browser.opera&&name=="display"){var save=style.outline;style.outline="0 solid black";style.outline=save;}if(name.match(/float/i))name=styleFloat;if(!force&&style&&style[name])ret=style[name];else if(defaultView.getComputedStyle){if(name.match(/float/i))name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle&&!color(elem))ret=computedStyle.getPropertyValue(name);else{var swap=[],stack=[],a=elem,i=0;for(;a&&color(a);a=a.parentNode)stack.unshift(a);for(;i<stack.length;i++)if(color(stack[i])){swap[i]=stack[i].style.display;stack[i].style.display="block";}ret=name=="display"&&swap[stack.length-1]!=null?"none":(computedStyle&&computedStyle.getPropertyValue(name))||"";for(i=0;i<swap.length;i++)if(swap[i]!=null)stack[i].style.display=swap[i];}if(name=="opacity"&&ret=="")ret="1";}else if(elem.currentStyle){var camelCase=name.replace(/\-(\w)/g,function(all,letter){return letter.toUpperCase();});ret=elem.currentStyle[name]||elem.currentStyle[camelCase];if(!/^\d+(px)?$/i.test(ret)&&/^\d/.test(ret)){var left=style.left,rsLeft=elem.runtimeStyle.left;elem.runtimeStyle.left=elem.currentStyle.left;style.left=ret||0;ret=style.pixelLeft+"px";style.left=left;elem.runtimeStyle.left=rsLeft;}}return ret;},clean:function(elems,context){var ret=[];context=context||document;if(typeof context.createElement=='undefined')context=context.ownerDocument||context[0]&&context[0].ownerDocument||document;jQuery.each(elems,function(i,elem){if(!elem)return;if(elem.constructor==Number)elem+='';if(typeof elem=="string"){elem=elem.replace(/(<(\w+)[^>]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+"></"+tag+">";});var tags=jQuery.trim(elem).toLowerCase(),div=context.createElement("div");var wrap=!tags.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!tags.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!tags.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!tags.indexOf("<td")||!tags.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!tags.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||jQuery.browser.msie&&[1,"div<div>","</div>"]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery.browser.msie){var tbody=!tags.indexOf("<table")&&tags.indexOf("<tbody")<0?div.firstChild&&div.firstChild.childNodes:wrap[1]=="<table>"&&tags.indexOf("<tbody")<0?div.childNodes:[];for(var j=tbody.length-1;j>=0;--j)if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length)tbody[j].parentNode.removeChild(tbody[j]);if(/^\s/.test(elem))div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);}elem=jQuery.makeArray(div.childNodes);}if(elem.length===0&&(!jQuery.nodeName(elem,"form")&&!jQuery.nodeName(elem,"select")))return;if(elem[0]==undefined||jQuery.nodeName(elem,"form")||elem.options)ret.push(elem);else
-ret=jQuery.merge(ret,elem);});return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8)return undefined;var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined,msie=jQuery.browser.msie;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(name in elem&&notxml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem[name]=value;}if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name))return elem.getAttributeNode(name).nodeValue;return elem[name];}if(msie&&notxml&&name=="style")return jQuery.attr(elem.style,"cssText",value);if(set)elem.setAttribute(name,""+value);var attr=msie&&notxml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr;}if(msie&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(value)+''=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+'':"";}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(set)elem[name]=value;return elem[name];},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||array.split||array.setInterval||array.call)ret[0]=array;else
-while(i)ret[--i]=array[i];}return ret;},inArray:function(elem,array){for(var i=0,length=array.length;i<length;i++)if(array[i]===elem)return i;return-1;},merge:function(first,second){var i=0,elem,pos=first.length;if(jQuery.browser.msie){while(elem=second[i++])if(elem.nodeType!=8)first[pos++]=elem;}else
-while(elem=second[i++])first[pos++]=elem;return first;},unique:function(array){var ret=[],done={};try{for(var i=0,length=array.length;i<length;i++){var id=jQuery.data(array[i]);if(!done[id]){done[id]=true;ret.push(array[i]);}}}catch(e){ret=array;}return ret;},grep:function(elems,callback,inv){var ret=[];for(var i=0,length=elems.length;i<length;i++)if(!inv!=!callback(elems[i],i))ret.push(elems[i]);return ret;},map:function(elems,callback){var ret=[];for(var i=0,length=elems.length;i<length;i++){var value=callback(elems[i],i);if(value!=null)ret[ret.length]=value;}return ret.concat.apply([],ret);}});var userAgent=navigator.userAgent.toLowerCase();jQuery.browser={version:(userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[])[1],safari:/webkit/.test(userAgent),opera:/opera/.test(userAgent),msie:/msie/.test(userAgent)&&!/opera/.test(userAgent),mozilla:/mozilla/.test(userAgent)&&!/(compatible|webkit)/.test(userAgent)};var styleFloat=jQuery.browser.msie?"styleFloat":"cssFloat";jQuery.extend({boxModel:!jQuery.browser.msie||document.compatMode=="CSS1Compat",props:{"for":"htmlFor","class":"className","float":styleFloat,cssFloat:styleFloat,styleFloat:styleFloat,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing"}});jQuery.each({parent:function(elem){return elem.parentNode;},parents:function(elem){return jQuery.dir(elem,"parentNode");},next:function(elem){return jQuery.nth(elem,2,"nextSibling");},prev:function(elem){return jQuery.nth(elem,2,"previousSibling");},nextAll:function(elem){return jQuery.dir(elem,"nextSibling");},prevAll:function(elem){return jQuery.dir(elem,"previousSibling");},siblings:function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},children:function(elem){return jQuery.sibling(elem.firstChild);},contents:function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}},function(name,fn){jQuery.fn[name]=function(selector){var ret=jQuery.map(this,fn);if(selector&&typeof selector=="string")ret=jQuery.multiFilter(selector,ret);return this.pushStack(jQuery.unique(ret));};});jQuery.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(name,original){jQuery.fn[name]=function(){var args=arguments;return this.each(function(){for(var i=0,length=args.length;i<length;i++)jQuery(args[i])[original](this);});};});jQuery.each({removeAttr:function(name){jQuery.attr(this,name,"");if(this.nodeType==1)this.removeAttribute(name);},addClass:function(classNames){jQuery.className.add(this,classNames);},removeClass:function(classNames){jQuery.className.remove(this,classNames);},toggleClass:function(classNames){jQuery.className[jQuery.className.has(this,classNames)?"remove":"add"](this,classNames);},remove:function(selector){if(!selector||jQuery.filter(selector,[this]).r.length){jQuery("*",this).add(this).each(function(){jQuery.event.remove(this);jQuery.removeData(this);});if(this.parentNode)this.parentNode.removeChild(this);}},empty:function(){jQuery(">*",this).remove();while(this.firstChild)this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?jQuery.browser.opera&&document.body["client"+name]||jQuery.browser.safari&&window["inner"+name]||document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(Math.max(document.body["scroll"+name],document.documentElement["scroll"+name]),Math.max(document.body["offset"+name],document.documentElement["offset"+name])):size==undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,size.constructor==String?size:size+"px");};});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0;}var chars=jQuery.browser.safari&&parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",quickChild=new RegExp("^>\\s*("+chars+"+)"),quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)"),quickClass=new RegExp("^([#.]?)("+chars+"*)");jQuery.extend({expr:{"":function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},"#":function(a,i,m){return a.getAttribute("id")==m[2];},":":{lt:function(a,i,m){return i<m[3]-0;},gt:function(a,i,m){return i>m[3]-0;},nth:function(a,i,m){return m[3]-0==i;},eq:function(a,i,m){return m[3]-0==i;},first:function(a,i){return i==0;},last:function(a,i,m,r){return i==r.length-1;},even:function(a,i){return i%2==0;},odd:function(a,i){return i%2;},"first-child":function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},"last-child":function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},"only-child":function(a){return!jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},parent:function(a){return a.firstChild;},empty:function(a){return!a.firstChild;},contains:function(a,i,m){return(a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},visible:function(a){return"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},hidden:function(a){return"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},enabled:function(a){return!a.disabled;},disabled:function(a){return a.disabled;},checked:function(a){return a.checked;},selected:function(a){return a.selected||jQuery.attr(a,"selected");},text:function(a){return"text"==a.type;},radio:function(a){return"radio"==a.type;},checkbox:function(a){return"checkbox"==a.type;},file:function(a){return"file"==a.type;},password:function(a){return"password"==a.type;},submit:function(a){return"submit"==a.type;},image:function(a){return"image"==a.type;},reset:function(a){return"reset"==a.type;},button:function(a){return"button"==a.type||jQuery.nodeName(a,"button");},input:function(a){return/input|select|textarea|button/i.test(a.nodeName);},has:function(a,i,m){return jQuery.find(m[3],a).length;},header:function(a){return/h\d/i.test(a.nodeName);},animated:function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}}},parse:[/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,new RegExp("^([:.#]*)("+chars+"+)")],multiFilter:function(expr,elems,not){var old,cur=[];while(expr&&expr!=old){old=expr;var f=jQuery.filter(expr,elems,not);expr=f.t.replace(/^\s*,\s*/,"");cur=not?elems=f.r:jQuery.merge(cur,f.r);}return cur;},find:function(t,context){if(typeof t!="string")return[t];if(context&&context.nodeType!=1&&context.nodeType!=9)return[];context=context||document;var ret=[context],done=[],last,nodeName;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false,re=quickChild,m=re.exec(t);if(m){nodeName=m[1].toUpperCase();for(var i=0;ret[i];i++)for(var c=ret[i].firstChild;c;c=c.nextSibling)if(c.nodeType==1&&(nodeName=="*"||c.nodeName.toUpperCase()==nodeName))r.push(c);ret=r;t=t.replace(re,"");if(t.indexOf(" ")==0)continue;foundToken=true;}else{re=/^([>+~])\s*(\w*)/i;if((m=re.exec(t))!=null){r=[];var merge={};nodeName=m[2].toUpperCase();m=m[1];for(var j=0,rl=ret.length;j<rl;j++){var n=m=="~"||m=="+"?ret[j].nextSibling:ret[j].firstChild;for(;n;n=n.nextSibling)if(n.nodeType==1){var id=jQuery.data(n);if(m=="~"&&merge[id])break;if(!nodeName||n.nodeName.toUpperCase()==nodeName){if(m=="~")merge[id]=true;r.push(n);}if(m=="+")break;}}ret=r;t=jQuery.trim(t.replace(re,""));foundToken=true;}}if(t&&!foundToken){if(!t.indexOf(",")){if(context==ret[0])ret.shift();done=jQuery.merge(done,ret);r=ret=[context];t=" "+t.substr(1,t.length);}else{var re2=quickID;var m=re2.exec(t);if(m){m=[0,m[2],m[3],m[1]];}else{re2=quickClass;m=re2.exec(t);}m[2]=m[2].replace(/\\/g,"");var elem=ret[ret.length-1];if(m[1]=="#"&&elem&&elem.getElementById&&!jQuery.isXMLDoc(elem)){var oid=elem.getElementById(m[2]);if((jQuery.browser.msie||jQuery.browser.opera)&&oid&&typeof oid.id=="string"&&oid.id!=m[2])oid=jQuery('[@id="'+m[2]+'"]',elem)[0];ret=r=oid&&(!m[3]||jQuery.nodeName(oid,m[3]))?[oid]:[];}else{for(var i=0;ret[i];i++){var tag=m[1]=="#"&&m[3]?m[3]:m[1]!=""||m[0]==""?"*":m[2];if(tag=="*"&&ret[i].nodeName.toLowerCase()=="object")tag="param";r=jQuery.merge(r,ret[i].getElementsByTagName(tag));}if(m[1]==".")r=jQuery.classFilter(r,m[2]);if(m[1]=="#"){var tmp=[];for(var i=0;r[i];i++)if(r[i].getAttribute("id")==m[2]){tmp=[r[i]];break;}r=tmp;}ret=r;}t=t.replace(re2,"");}}if(t){var val=jQuery.filter(t,r);ret=r=val.r;t=jQuery.trim(val.t);}}if(t)ret=[];if(ret&&context==ret[0])ret.shift();done=jQuery.merge(done,ret);return done;},classFilter:function(r,m,not){m=" "+m+" ";var tmp=[];for(var i=0;r[i];i++){var pass=(" "+r[i].className+" ").indexOf(m)>=0;if(!not&&pass||not&&!pass)tmp.push(r[i]);}return tmp;},filter:function(t,r,not){var last;while(t&&t!=last){last=t;var p=jQuery.parse,m;for(var i=0;p[i];i++){m=p[i].exec(t);if(m){t=t.substring(m[0].length);m[2]=m[2].replace(/\\/g,"");break;}}if(!m)break;if(m[1]==":"&&m[2]=="not")r=isSimple.test(m[3])?jQuery.filter(m[3],r,true).r:jQuery(r).not(m[3]);else if(m[1]==".")r=jQuery.classFilter(r,m[2],not);else if(m[1]=="["){var tmp=[],type=m[3];for(var i=0,rl=r.length;i<rl;i++){var a=r[i],z=a[jQuery.props[m[2]]||m[2]];if(z==null||/href|src|selected/.test(m[2]))z=jQuery.attr(a,m[2])||'';if((type==""&&!!z||type=="="&&z==m[5]||type=="!="&&z!=m[5]||type=="^="&&z&&!z.indexOf(m[5])||type=="$="&&z.substr(z.length-m[5].length)==m[5]||(type=="*="||type=="~=")&&z.indexOf(m[5])>=0)^not)tmp.push(a);}r=tmp;}else if(m[1]==":"&&m[2]=="nth-child"){var merge={},tmp=[],test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3]=="even"&&"2n"||m[3]=="odd"&&"2n+1"||!/\D/.test(m[3])&&"0n+"+m[3]||m[3]),first=(test[1]+(test[2]||1))-0,last=test[3]-0;for(var i=0,rl=r.length;i<rl;i++){var node=r[i],parentNode=node.parentNode,id=jQuery.data(parentNode);if(!merge[id]){var c=1;for(var n=parentNode.firstChild;n;n=n.nextSibling)if(n.nodeType==1)n.nodeIndex=c++;merge[id]=true;}var add=false;if(first==0){if(node.nodeIndex==last)add=true;}else if((node.nodeIndex-last)%first==0&&(node.nodeIndex-last)/first>=0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var fn=jQuery.expr[m[1]];if(typeof fn=="object")fn=fn[m[2]];if(typeof fn=="string")fn=eval("false||function(a,i){return "+fn+";}");r=jQuery.grep(r,function(elem,i){return fn(elem,i,m,r);},not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1)matched.push(cur);cur=cur[dir];}return matched;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir])if(cur.nodeType==1&&++num==result)break;return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem)r.push(n);}return r;}});jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8)return;if(jQuery.browser.msie&&elem.setInterval)elem=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=this.proxy(fn,function(){return fn.apply(this,arguments);});handler.data=data;}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){if(typeof jQuery!="undefined"&&!jQuery.event.triggered)return jQuery.event.handle.apply(arguments.callee.elem,arguments);});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];handler.type=parts[1];var handlers=events[type];if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem)===false){if(elem.addEventListener)elem.addEventListener(type,handle,false);else if(elem.attachEvent)elem.attachEvent("on"+type,handle);}}handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8)return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types==undefined||(typeof types=="string"&&types.charAt(0)=="."))for(var type in events)this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;}jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];if(events[type]){if(handler)delete events[type][handler.guid];else
-for(handler in events[type])if(!parts[1]||events[type][handler].type==parts[1])delete events[type][handler];for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem)===false){if(elem.removeEventListener)elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent)elem.detachEvent("on"+type,jQuery.data(elem,"handle"));}ret=null;delete events[type];}}});}for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(type,data,elem,donative,extra){data=jQuery.makeArray(data);if(type.indexOf("!")>=0){type=type.slice(0,-1);var exclusive=true;}if(!elem){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{if(elem.nodeType==3||elem.nodeType==8)return undefined;var val,ret,fn=jQuery.isFunction(elem[type]||null),event=!data[0]||!data[0].preventDefault;if(event){data.unshift({type:type,target:elem,preventDefault:function(){},stopPropagation:function(){},timeStamp:now()});data[0][expando]=true;}data[0].type=type;if(exclusive)data[0].exclusive=true;var handle=jQuery.data(elem,"handle");if(handle)val=handle.apply(elem,data);if((!fn||(jQuery.nodeName(elem,'a')&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false)val=false;if(event)data.shift();if(extra&&jQuery.isFunction(extra)){ret=extra.apply(elem,val==null?data:data.concat(val));if(ret!==undefined)val=ret;}if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}}this.triggered=false;}return val;},handle:function(event){var val,ret,namespace,all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);namespace=event.type.split(".");event.type=namespace[0];namespace=namespace[1];all=!namespace&&!event.exclusive;handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||handler.type==namespace){event.handler=handler;event.data=handler.data;ret=handler.apply(this,arguments);if(val!==false)val=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}return val;},fix:function(event){if(event[expando]==true)return event;var originalEvent=event;event={originalEvent:originalEvent};var props="altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target timeStamp toElement type view wheelDelta which".split(" ");for(var i=props.length;i;i--)event[props[i]]=originalEvent[props[i]];event[expando]=true;event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};event.timeStamp=event.timeStamp||now();if(!event.target)event.target=event.srcElement||document;if(event.target.nodeType==3)event.target=event.target.parentNode;if(!event.relatedTarget&&event.fromElement)event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode))event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey)event.metaKey=event.ctrlKey;if(!event.which&&event.button)event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},proxy:function(fn,proxy){proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy;},special:{ready:{setup:function(){bindReady();return;},teardown:function(){return;}},mouseenter:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseover",jQuery.event.special.mouseenter.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseover",jQuery.event.special.mouseenter.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseenter";return jQuery.event.handle.apply(this,arguments);}},mouseleave:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseout",jQuery.event.special.mouseleave.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseout",jQuery.event.special.mouseleave.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseleave";return jQuery.event.handle.apply(this,arguments);}}}};jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments);});return this.each(function(){jQuery.event.add(this,type,one,fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data,fn){return this.each(function(){jQuery.event.trigger(type,data,this,true,fn);});},triggerHandler:function(type,data,fn){return this[0]&&jQuery.event.trigger(type,data,this[0],false,fn);},toggle:function(fn){var args=arguments,i=1;while(i<args.length)jQuery.event.proxy(fn,args[i++]);return this.click(jQuery.event.proxy(fn,function(event){this.lastToggle=(this.lastToggle||0)%i;event.preventDefault();return args[this.lastToggle++].apply(this,arguments)||false;}));},hover:function(fnOver,fnOut){return this.bind('mouseenter',fnOver).bind('mouseleave',fnOut);},ready:function(fn){bindReady();if(jQuery.isReady)fn.call(document,jQuery);else
-jQuery.readyList.push(function(){return fn.call(this,jQuery);});return this;}});jQuery.extend({isReady:false,readyList:[],ready:function(){if(!jQuery.isReady){jQuery.isReady=true;if(jQuery.readyList){jQuery.each(jQuery.readyList,function(){this.call(document);});jQuery.readyList=null;}jQuery(document).triggerHandler("ready");}}});var readyBound=false;function bindReady(){if(readyBound)return;readyBound=true;if(document.addEventListener&&!jQuery.browser.opera)document.addEventListener("DOMContentLoaded",jQuery.ready,false);if(jQuery.browser.msie&&window==top)(function(){if(jQuery.isReady)return;try{document.documentElement.doScroll("left");}catch(error){setTimeout(arguments.callee,0);return;}jQuery.ready();})();if(jQuery.browser.opera)document.addEventListener("DOMContentLoaded",function(){if(jQuery.isReady)return;for(var i=0;i<document.styleSheets.length;i++)if(document.styleSheets[i].disabled){setTimeout(arguments.callee,0);return;}jQuery.ready();},false);if(jQuery.browser.safari){var numStyles;(function(){if(jQuery.isReady)return;if(document.readyState!="loaded"&&document.readyState!="complete"){setTimeout(arguments.callee,0);return;}if(numStyles===undefined)numStyles=jQuery("style, link[rel=stylesheet]").length;if(document.styleSheets.length!=numStyles){setTimeout(arguments.callee,0);return;}jQuery.ready();})();}jQuery.event.add(window,"load",jQuery.ready);}jQuery.each(("blur,focus,load,resize,scroll,unload,click,dblclick,"+"mousedown,mouseup,mousemove,mouseover,mouseout,change,select,"+"submit,keydown,keypress,keyup,error").split(","),function(i,name){jQuery.fn[name]=function(fn){return fn?this.bind(name,fn):this.trigger(name);};});var withinElement=function(event,elem){var parent=event.relatedTarget;while(parent&&parent!=elem)try{parent=parent.parentNode;}catch(error){parent=elem;}return parent==elem;};jQuery(window).bind("unload",function(){jQuery("*").add(document).unbind();});jQuery.fn.extend({_load:jQuery.fn.load,load:function(url,params,callback){if(typeof url!='string')return this._load(url);var off=url.indexOf(" ");if(off>=0){var selector=url.slice(off,url.length);url=url.slice(0,off);}callback=callback||function(){};var type="GET";if(params)if(jQuery.isFunction(params)){callback=params;params=null;}else{params=jQuery.param(params);type="POST";}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified")self.html(selector?jQuery("<div/>").append(res.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(selector):res.responseText);self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return jQuery.nodeName(this,"form")?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:val.constructor==Array?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{url:location.href,global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null,username:null,password:null,accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre))s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre))s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data)s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}if(head)head.removeChild(script);};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");}if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");var remote=/^(?:\w+:)?\/\/([^\/?#]+)/;if(s.dataType=="script"&&type=="GET"&&remote.test(s.url)&&remote.exec(s.url)[1]!=location.host){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset)script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();head.removeChild(script);}};}head.appendChild(script);return undefined;}var requestDone=false;var xhr=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();if(s.username)xhr.open(type,s.url,s.async,s.username,s.password);else
-xhr.open(type,s.url,s.async);try{if(s.data)xhr.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){}if(s.beforeSend&&s.beforeSend(xhr,s)===false){s.global&&jQuery.active--;xhr.abort();return false;}if(s.global)jQuery.event.trigger("ajaxSend",[xhr,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpSuccess(xhr)&&"error"||s.ifModified&&jQuery.httpNotModified(xhr,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s.dataFilter);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else
-jQuery.handleError(s,xhr,status);complete();if(s.async)xhr=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xhr){xhr.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xhr.send(s.data);}catch(e){jQuery.handleError(s,xhr,null,e);}if(!s.async)onreadystatechange();function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xhr,s]);}function complete(){if(s.complete)s.complete(xhr,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xhr,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}return xhr;},handleError:function(s,xhr,status,e){if(s.error)s.error(xhr,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xhr,s,e]);},active:0,httpSuccess:function(xhr){try{return!xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url]||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpData:function(xhr,type,filter){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";if(filter)data=filter(data,type);if(type=="script")jQuery.globalEval(data);if(type=="json")data=eval("("+data+")");return data;},param:function(a){var s=[];if(a.constructor==Array||a.jquery)jQuery.each(a,function(){s.push(encodeURIComponent(this.name)+"="+encodeURIComponent(this.value));});else
-for(var j in a)if(a[j]&&a[j].constructor==Array)jQuery.each(a[j],function(){s.push(encodeURIComponent(j)+"="+encodeURIComponent(this));});else
-s.push(encodeURIComponent(j)+"="+encodeURIComponent(jQuery.isFunction(a[j])?a[j]():a[j]));return s.join("&").replace(/%20/g,"+");}});jQuery.fn.extend({show:function(speed,callback){return speed?this.animate({height:"show",width:"show",opacity:"show"},speed,callback):this.filter(":hidden").each(function(){this.style.display=this.oldblock||"";if(jQuery.css(this,"display")=="none"){var elem=jQuery("<"+this.tagName+" />").appendTo("body");this.style.display=elem.css("display");if(this.style.display=="none")this.style.display="block";elem.remove();}}).end();},hide:function(speed,callback){return speed?this.animate({height:"hide",width:"hide",opacity:"hide"},speed,callback):this.filter(":visible").each(function(){this.oldblock=this.oldblock||jQuery.css(this,"display");this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle.apply(this,arguments):fn?this.animate({height:"toggle",width:"toggle",opacity:"toggle"},fn,fn2):this.each(function(){jQuery(this)[jQuery(this).is(":hidden")?"show":"hide"]();});},slideDown:function(speed,callback){return this.animate({height:"show"},speed,callback);},slideUp:function(speed,callback){return this.animate({height:"hide"},speed,callback);},slideToggle:function(speed,callback){return this.animate({height:"toggle"},speed,callback);},fadeIn:function(speed,callback){return this.animate({opacity:"show"},speed,callback);},fadeOut:function(speed,callback){return this.animate({opacity:"hide"},speed,callback);},fadeTo:function(speed,to,callback){return this.animate({opacity:to},speed,callback);},animate:function(prop,speed,easing,callback){var optall=jQuery.speed(speed,easing,callback);return this[optall.queue===false?"each":"queue"](function(){if(this.nodeType!=1)return false;var opt=jQuery.extend({},optall),p,hidden=jQuery(this).is(":hidden"),self=this;for(p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return opt.complete.call(this);if(p=="height"||p=="width"){opt.display=jQuery.css(this,"display");opt.overflow=this.style.overflow;}}if(opt.overflow!=null)this.style.overflow="hidden";opt.curAnim=jQuery.extend({},prop);jQuery.each(prop,function(name,val){var e=new jQuery.fx(self,opt,name);if(/toggle|show|hide/.test(val))e[val=="toggle"?hidden?"show":"hide":val](prop);else{var parts=val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),start=e.cur(true)||0;if(parts){var end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=(end||1)+unit;start=((end||1)/e.cur(true))*start;self.style[name]=start+unit;}if(parts[1])end=((parts[1]=="-="?-1:1)*end)+start;e.custom(start,end,unit);}else
-e.custom(start,val,"");}});return true;});},queue:function(type,fn){if(jQuery.isFunction(type)||(type&&type.constructor==Array)){fn=type;type="fx";}if(!type||(typeof type=="string"&&!fn))return queue(this[0],type);return this.each(function(){if(fn.constructor==Array)queue(this,type,fn);else{queue(this,type).push(fn);if(queue(this,type).length==1)fn.call(this);}});},stop:function(clearQueue,gotoEnd){var timers=jQuery.timers;if(clearQueue)this.queue([]);this.each(function(){for(var i=timers.length-1;i>=0;i--)if(timers[i].elem==this){if(gotoEnd)timers[i](true);timers.splice(i,1);}});if(!gotoEnd)this.dequeue();return this;}});var queue=function(elem,type,array){if(elem){type=type||"fx";var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",jQuery.makeArray(array));}return q;};jQuery.fn.dequeue=function(type){type=type||"fx";return this.each(function(){var q=queue(this,type);q.shift();if(q.length)q[0].call(this);});};jQuery.extend({speed:function(speed,easing,fn){var opt=speed&&speed.constructor==Object?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&easing.constructor!=Function&&easing};opt.duration=(opt.duration&&opt.duration.constructor==Number?opt.duration:jQuery.fx.speeds[opt.duration])||jQuery.fx.speeds.def;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false)jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.call(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],timerId:null,fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig)options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step)this.options.step.call(this.elem,this.now,this);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if(this.prop=="height"||this.prop=="width")this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&this.elem.style[this.prop]==null)return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;this.update();var self=this;function t(gotoEnd){return self.step(gotoEnd);}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timerId==null){jQuery.timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;i<timers.length;i++)if(!timers[i]())timers.splice(i--,1);if(!timers.length){clearInterval(jQuery.timerId);jQuery.timerId=null;}},13);}},show:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.show=true;this.custom(0,this.cur());if(this.prop=="width"||this.prop=="height")this.elem.style[this.prop]="1px";jQuery(this.elem).show();},hide:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0);},step:function(gotoEnd){var t=now();if(gotoEnd||t>this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim)if(this.options.curAnim[i]!==true)done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none")this.elem.style.display="block";}if(this.options.hide)this.elem.style.display="none";if(this.options.hide||this.options.show)for(var p in this.options.curAnim)jQuery.attr(this.elem.style,p,this.options.orig[p]);}if(done)this.options.complete.call(this.elem);return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();}return true;}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,def:400},step:{scrollLeft:function(fx){fx.elem.scrollLeft=fx.now;},scrollTop:function(fx){fx.elem.scrollTop=fx.now;},opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){fx.elem.style[fx.prop]=fx.now+fx.unit;}}});jQuery.fn.offset=function(){var left=0,top=0,elem=this[0],results;if(elem)with(jQuery.browser){var parent=elem.parentNode,offsetChild=elem,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&parseInt(version)<522&&!/adobeair/i.test(userAgent),css=jQuery.curCSS,fixed=css(elem,"position")=="fixed";if(elem.getBoundingClientRect){var box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));add(-doc.documentElement.clientLeft,-doc.documentElement.clientTop);}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&!/^t(able|d|h)$/i.test(offsetParent.tagName)||safari&&!safari2)border(offsetParent);if(!fixed&&css(offsetParent,"position")=="fixed")fixed=true;offsetChild=/^body$/i.test(offsetParent.tagName)?offsetChild:offsetParent;offsetParent=offsetParent.offsetParent;}while(parent&&parent.tagName&&!/^body|html$/i.test(parent.tagName)){if(!/^inline|table.*$/i.test(css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if((safari2&&(fixed||css(offsetChild,"position")=="absolute"))||(mozilla&&css(offsetChild,"position")!="absolute"))add(-doc.body.offsetLeft,-doc.body.offsetTop);if(fixed)add(Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));}results={top:top,left:left};}function border(elem){add(jQuery.curCSS(elem,"borderLeftWidth",true),jQuery.curCSS(elem,"borderTopWidth",true));}function add(l,t){left+=parseInt(l,10)||0;top+=parseInt(t,10)||0;}return results;};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,'marginTop');offset.left-=num(this,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};}return results;},offsetParent:function(){var offsetParent=this[0].offsetParent;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,'position')=='static'))offsetParent=offsetParent.offsetParent;return jQuery(offsetParent);}});jQuery.each(['Left','Top'],function(i,name){var method='scroll'+name;jQuery.fn[method]=function(val){if(!this[0])return;return val!=undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val;}):this[0]==window||this[0]==document?self[i?'pageYOffset':'pageXOffset']||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method];};});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom";jQuery.fn["inner"+name]=function(){return this[name.toLowerCase()]()+num(this,"padding"+tl)+num(this,"padding"+br);};jQuery.fn["outer"+name]=function(margin){return this["inner"+name]()+num(this,"border"+tl+"Width")+num(this,"border"+br+"Width")+(margin?num(this,"margin"+tl)+num(this,"margin"+br):0);};});})();
\ No newline at end of file
+(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F<J;F++){var G=M[F];if(G.selected){K=o(G).val();if(H){return K}L.push(K)}}return L}return(E.value||"").replace(/\r/g,"")}return g}if(typeof K==="number"){K+=""}return this.each(function(){if(this.nodeType!=1){return}if(o.isArray(K)&&/radio|checkbox/.test(this.type)){this.checked=(o.inArray(this.value,K)>=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G<E;G++){L.call(K(this[G],H),this.length>1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H<I;H++){if((G=arguments[H])!=null){for(var F in G){var K=J[F],L=G[F];if(J===L){continue}if(E&&L&&typeof L==="object"&&!L.nodeType){J[F]=o.extend(E,K||(L.length!=null?[]:{}),L)}else{if(L!==g){J[F]=L}}}}}return J};var b=/z-?index|font-?weight|opacity|zoom|line-?height/i,q=document.defaultView||{},s=Object.prototype.toString;o.extend({noConflict:function(E){l.$=p;if(E){l.jQuery=y}return o},isFunction:function(E){return s.call(E)==="[object Function]"},isArray:function(E){return s.call(E)==="[object Array]"},isXMLDoc:function(E){return E.nodeType===9&&E.documentElement.nodeName!=="HTML"||!!E.ownerDocument&&o.isXMLDoc(E.ownerDocument)},globalEval:function(G){if(G&&/\S/.test(G)){var F=document.getElementsByTagName("head")[0]||document.documentElement,E=document.createElement("script");E.type="text/javascript";if(o.support.scriptEval){E.appendChild(document.createTextNode(G))}else{E.text=G}F.insertBefore(E,F.firstChild);F.removeChild(E)}},nodeName:function(F,E){return F.nodeName&&F.nodeName.toUpperCase()==E.toUpperCase()},each:function(G,K,F){var E,H=0,I=G.length;if(F){if(I===g){for(E in G){if(K.apply(G[E],F)===false){break}}}else{for(;H<I;){if(K.apply(G[H++],F)===false){break}}}}else{if(I===g){for(E in G){if(K.call(G[E],E,G[E])===false){break}}}else{for(var J=G[0];H<I&&K.call(J,H,J)!==false;J=G[++H]){}}}return G},prop:function(H,I,G,F,E){if(o.isFunction(I)){I=I.call(H,F)}return typeof I==="number"&&G=="curCSS"&&!b.test(E)?I+"px":I},className:{add:function(E,F){o.each((F||"").split(/\s+/),function(G,H){if(E.nodeType==1&&!o.className.has(E.className,H)){E.className+=(E.className?" ":"")+H}})},remove:function(E,F){if(E.nodeType==1){E.className=F!==g?o.grep(E.className.split(/\s+/),function(G){return !o.className.has(F,G)}).join(" "):""}},has:function(F,E){return F&&o.inArray(E,(F.className||F).toString().split(/\s+/))>-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+"></"+T+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!O.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!O.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!O.indexOf("<td")||!O.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!O.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||!o.support.htmlSerialize&&[1,"div<div>","</div>"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/<tbody/i.test(S),N=!O.indexOf("<table")&&!R?L.firstChild&&L.firstChild.childNodes:Q[1]=="<table>"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E<F;E++){if(H[E]===G){return E}}return -1},merge:function(H,E){var F=0,G,I=H.length;if(!o.support.getAll){while((G=E[F++])!=null){if(G.nodeType!=8){H[I++]=G}}}else{while((G=E[F++])!=null){H[I++]=G}}return H},unique:function(K){var F=[],E={};try{for(var G=0,H=K.length;G<H;G++){var J=o.data(K[G]);if(!E[J]){E[J]=true;F.push(K[G])}}}catch(I){F=K}return F},grep:function(F,J,E){var G=[];for(var H=0,I=F.length;H<I;H++){if(!E!=!J(F[H],H)){G.push(F[H])}}return G},map:function(E,J){var F=[];for(var G=0,H=E.length;G<H;G++){var I=J(E[G],G);if(I!=null){F[F.length]=I}}return F.concat.apply([],F)}});var C=navigator.userAgent.toLowerCase();o.browser={version:(C.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[0,"0"])[1],safari:/webkit/.test(C),opera:/opera/.test(C),msie:/msie/.test(C)&&!/opera/.test(C),mozilla:/mozilla/.test(C)&&!/(compatible|webkit)/.test(C)};o.each({parent:function(E){return E.parentNode},parents:function(E){return o.dir(E,"parentNode")},next:function(E){return o.nth(E,2,"nextSibling")},prev:function(E){return o.nth(E,2,"previousSibling")},nextAll:function(E){return o.dir(E,"nextSibling")},prevAll:function(E){return o.dir(E,"previousSibling")},siblings:function(E){return o.sibling(E.parentNode.firstChild,E)},children:function(E){return o.sibling(E.firstChild)},contents:function(E){return o.nodeName(E,"iframe")?E.contentDocument||E.contentWindow.document:o.makeArray(E.childNodes)}},function(E,F){o.fn[E]=function(G){var H=o.map(this,F);if(G&&typeof G=="string"){H=o.multiFilter(G,H)}return this.pushStack(o.unique(H),E,G)}});o.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(E,F){o.fn[E]=function(G){var J=[],L=o(G);for(var K=0,H=L.length;K<H;K++){var I=(K>0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}});
+/*
+ * Sizzle CSS Selector Engine - v0.9.3
+ *  Copyright 2009, The Dojo Foundation
+ *  Released under the MIT, BSD, and GPL Licenses.
+ *  More information: http://sizzlejs.com/
+ */
+(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa<ab.length;aa++){if(ab[aa]===ab[aa-1]){ab.splice(aa--,1)}}}}}return ab};F.matches=function(T,U){return F(T,null,null,U)};F.find=function(aa,T,ab){var Z,X;if(!aa){return[]}for(var W=0,V=I.order.length;W<V;W++){var Y=I.order[W],X;if((X=I.match[Y].exec(aa))){var U=RegExp.leftContext;if(U.substr(U.length-1)!=="\\"){X[1]=(X[1]||"").replace(/\\/g,"");Z=I.find[Y](X,T,ab);if(Z!=null){aa=aa.replace(I.match[Y],"");break}}}}if(!Z){Z=T.getElementsByTagName("*")}return{set:Z,expr:aa}};F.filter=function(ad,ac,ag,W){var V=ad,ai=[],aa=ac,Y,T,Z=ac&&ac[0]&&Q(ac[0]);while(ad&&ac.length){for(var ab in I.filter){if((Y=I.match[ab].exec(ad))!=null){var U=I.filter[ab],ah,af;T=false;if(aa==ai){ai=[]}if(I.preFilter[ab]){Y=I.preFilter[ab](Y,aa,ag,ai,W,Z);if(!Y){T=ah=true}else{if(Y===true){continue}}}if(Y){for(var X=0;(af=aa[X])!=null;X++){if(af){ah=U(af,Y,X,aa);var ae=W^!!ah;if(ag&&ah!=null){if(ae){T=true}else{aa[X]=false}}else{if(ae){ai.push(af);T=true}}}}}if(ah!==g){if(!ag){aa=ai}ad=ad.replace(I.match[ab],"");if(!T){return[]}break}}}if(ad==V){if(T==null){throw"Syntax error, unrecognized expression: "+ad}else{break}}V=ad}return aa};var I=F.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(T){return T.getAttribute("href")}},relative:{"+":function(aa,T,Z){var X=typeof T==="string",ab=X&&!/\W/.test(T),Y=X&&!ab;if(ab&&!Z){T=T.toUpperCase()}for(var W=0,V=aa.length,U;W<V;W++){if((U=aa[W])){while((U=U.previousSibling)&&U.nodeType!==1){}aa[W]=Y||U&&U.nodeName===T?U||false:U===T}}if(Y){F.filter(T,aa,true)}},">":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){var W=Y.parentNode;Z[V]=W.nodeName===U?W:false}}}else{for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){Z[V]=X?Y.parentNode:Y.parentNode===U}}if(X){F.filter(U,Z,true)}}},"":function(W,U,Y){var V=L++,T=S;if(!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("parentNode",U,V,W,X,Y)},"~":function(W,U,Y){var V=L++,T=S;if(typeof U==="string"&&!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("previousSibling",U,V,W,X,Y)}},find:{ID:function(U,V,W){if(typeof V.getElementById!=="undefined"&&!W){var T=V.getElementById(U[1]);return T?[T]:[]}},NAME:function(V,Y,Z){if(typeof Y.getElementsByName!=="undefined"){var U=[],X=Y.getElementsByName(V[1]);for(var W=0,T=X.length;W<T;W++){if(X[W].getAttribute("name")===V[1]){U.push(X[W])}}return U.length===0?null:U}},TAG:function(T,U){return U.getElementsByTagName(T[1])}},preFilter:{CLASS:function(W,U,V,T,Z,aa){W=" "+W[1].replace(/\\/g,"")+" ";if(aa){return W}for(var X=0,Y;(Y=U[X])!=null;X++){if(Y){if(Z^(Y.className&&(" "+Y.className+" ").indexOf(W)>=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return U<T[3]-0},gt:function(V,U,T){return U>T[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W<T;W++){if(Y[W]===Z){return false}}return true}}}},CHILD:function(T,W){var Z=W[1],U=T;switch(Z){case"only":case"first":while(U=U.previousSibling){if(U.nodeType===1){return false}}if(Z=="first"){return true}U=T;case"last":while(U=U.nextSibling){if(U.nodeType===1){return false}}return true;case"nth":var V=W[2],ac=W[3];if(V==1&&ac==0){return true}var Y=W[0],ab=T.parentNode;if(ab&&(ab.sizcache!==Y||!T.nodeIndex)){var X=0;for(U=ab.firstChild;U;U=U.nextSibling){if(U.nodeType===1){U.nodeIndex=++X}}ab.sizcache=Y}var aa=T.nodeIndex-ac;if(V==0){return aa==0}else{return(aa%V==0&&aa/V>=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V<T;V++){U.push(X[V])}}else{for(var V=0;X[V];V++){U.push(X[V])}}}return U}}var G;if(document.documentElement.compareDocumentPosition){G=function(U,T){var V=U.compareDocumentPosition(T)&4?-1:U===T?0:1;if(V===0){hasDuplicate=true}return V}}else{if("sourceIndex" in document.documentElement){G=function(U,T){var V=U.sourceIndex-T.sourceIndex;if(V===0){hasDuplicate=true}return V}}else{if(document.createRange){G=function(W,U){var V=W.ownerDocument.createRange(),T=U.ownerDocument.createRange();V.selectNode(W);V.collapse(true);T.selectNode(U);T.collapse(true);var X=V.compareBoundaryPoints(Range.START_TO_END,T);if(X===0){hasDuplicate=true}return X}}}}(function(){var U=document.createElement("form"),V="script"+(new Date).getTime();U.innerHTML="<input name='"+V+"'/>";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="<a href='#'></a>";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="<p class='TEST'></p>";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="<div class='test e'></div><div class='test'></div>";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1&&!ac){T.sizcache=Y;T.sizset=W}if(T.nodeName===Z){X=T;break}T=T[U]}ad[W]=X}}}function S(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1){if(!ac){T.sizcache=Y;T.sizset=W}if(typeof Z!=="string"){if(T===Z){X=true;break}}else{if(F.filter(Z,[T]).length>0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z<U;Z++){F(T,V[Z],W)}return F.filter(X,W)};o.find=F;o.filter=F.filter;o.expr=F.selectors;o.expr[":"]=o.expr.filters;F.selectors.filters.hidden=function(T){return T.offsetWidth===0||T.offsetHeight===0};F.selectors.filters.visible=function(T){return T.offsetWidth>0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F<E.length){o.event.proxy(G,E[F++])}return this.click(o.event.proxy(G,function(H){this.lastToggle=(this.lastToggle||0)%F;H.preventDefault();return E[this.lastToggle++].apply(this,arguments)||false}))},hover:function(E,F){return this.mouseenter(E).mouseleave(F)},ready:function(E){B();if(o.isReady){E.call(document,o)}else{o.readyList.push(E)}return this},live:function(G,F){var E=o.event.proxy(F);E.guid+=this.selector+G;o(document).bind(i(G,this.selector),this.selector,E);return this},die:function(F,E){o(document).unbind(i(F,this.selector),E?{guid:E.guid+this.selector+F}:null);return this}});function c(H){var E=RegExp("(^|\\.)"+H.type+"(\\.|$)"),G=true,F=[];o.each(o.data(this,"events").live||[],function(I,J){if(E.test(J.type)){var K=o(H.target).closest(J.data)[0];if(K){F.push({elem:K,fn:J})}}});F.sort(function(J,I){return o.data(J.elem,"closest")-o.data(I.elem,"closest")});o.each(F,function(){if(this.fn.call(this.elem,H,this.fn.data)===false){return(G=false)}});return G}function i(F,E){return["live",F,E.replace(/\./g,"`").replace(/ /g,"|")].join(".")}o.extend({isReady:false,readyList:[],ready:function(){if(!o.isReady){o.isReady=true;if(o.readyList){o.each(o.readyList,function(){this.call(document,o)});o.readyList=null}o(document).triggerHandler("ready")}}});var x=false;function B(){if(x){return}x=true;if(document.addEventListener){document.addEventListener("DOMContentLoaded",function(){document.removeEventListener("DOMContentLoaded",arguments.callee,false);o.ready()},false)}else{if(document.attachEvent){document.attachEvent("onreadystatechange",function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",arguments.callee);o.ready()}});if(document.documentElement.doScroll&&l==l.top){(function(){if(o.isReady){return}try{document.documentElement.doScroll("left")}catch(E){setTimeout(arguments.callee,0);return}o.ready()})()}}}o.event.add(l,"load",o.ready)}o.each(("blur,focus,load,resize,scroll,unload,click,dblclick,mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave,change,select,submit,keydown,keypress,keyup,error").split(","),function(F,E){o.fn[E]=function(G){return G?this.bind(E,G):this.trigger(E)}});o(l).bind("unload",function(){for(var E in o.cache){if(E!=1&&o.cache[E].handle){o.event.remove(o.cache[E].handle.elem)}}});(function(){o.support={};var F=document.documentElement,G=document.createElement("script"),K=document.createElement("div"),J="script"+(new Date).getTime();K.style.display="none";K.innerHTML='   <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';var H=K.getElementsByTagName("*"),E=K.getElementsByTagName("a")[0];if(!H||!H.length||!E){return}o.support={leadingWhitespace:K.firstChild.nodeType==3,tbody:!K.getElementsByTagName("tbody").length,objectAll:!!K.getElementsByTagName("object")[0].getElementsByTagName("*").length,htmlSerialize:!!K.getElementsByTagName("link").length,style:/red/.test(E.getAttribute("style")),hrefNormalized:E.getAttribute("href")==="/a",opacity:E.style.opacity==="0.5",cssFloat:!!E.style.cssFloat,scriptEval:false,noCloneEvent:true,boxModel:null};G.type="text/javascript";try{G.appendChild(document.createTextNode("window."+J+"=1;"))}catch(I){}F.insertBefore(G,F.firstChild);if(l[J]){o.support.scriptEval=true;delete l[J]}F.removeChild(G);if(K.attachEvent&&K.fireEvent){K.attachEvent("onclick",function(){o.support.noCloneEvent=false;K.detachEvent("onclick",arguments.callee)});K.cloneNode(true).fireEvent("onclick")}o(function(){var L=document.createElement("div");L.style.width=L.style.paddingLeft="1px";document.body.appendChild(L);o.boxModel=o.support.boxModel=L.offsetWidth===2;document.body.removeChild(L).style.display="none"})})();var w=o.support.cssFloat?"cssFloat":"styleFloat";o.props={"for":"htmlFor","class":"className","float":w,cssFloat:w,styleFloat:w,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",tabindex:"tabIndex"};o.fn.extend({_load:o.fn.load,load:function(G,J,K){if(typeof G!=="string"){return this._load(G)}var I=G.indexOf(" ");if(I>=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("<div/>").append(M.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H<F;H++){var E=o.data(this[H],"olddisplay");this[H].style.display=E||"";if(o.css(this[H],"display")==="none"){var G=this[H].tagName,K;if(m[G]){K=m[G]}else{var I=o("<"+G+" />").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H<F;H++){this[H].style.display=o.data(this[H],"olddisplay")||""}return this}},hide:function(H,I){if(H){return this.animate(t("hide",3),H,I)}else{for(var G=0,F=this.length;G<F;G++){var E=o.data(this[G],"olddisplay");if(!E&&E!=="none"){o.data(this[G],"olddisplay",o.css(this[G],"display"))}}for(var G=0,F=this.length;G<F;G++){this[G].style.display="none"}return this}},_toggle:o.fn.toggle,toggle:function(G,F){var E=typeof G==="boolean";return o.isFunction(G)&&o.isFunction(F)?this._toggle.apply(this,arguments):G==null||E?this.each(function(){var H=E?G:o(this).is(":hidden");o(this)[H?"show":"hide"]()}):this.animate(t("toggle",3),G,F)},fadeTo:function(E,G,F){return this.animate({opacity:G},E,F)},animate:function(I,F,H,G){var E=o.speed(F,H,G);return this[E.queue===false?"each":"queue"](function(){var K=o.extend({},E),M,L=this.nodeType==1&&o(this).is(":hidden"),J=this;for(M in I){if(I[M]=="hide"&&L||I[M]=="show"&&!L){return K.complete.call(this)}if((M=="height"||M=="width")&&this.style){K.display=o.css(this,"display");K.overflow=this.style.overflow}}if(K.overflow!=null){this.style.overflow="hidden"}K.curAnim=o.extend({},I);o.each(I,function(O,S){var R=new o.fx(J,K,O);if(/toggle|show|hide/.test(S)){R[S=="toggle"?L?"show":"hide":S](I)}else{var Q=S.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),T=R.cur(true)||0;if(Q){var N=parseFloat(Q[2]),P=Q[3]||"px";if(P!="px"){J.style[O]=(N||1)+P;T=((N||1)/R.cur(true))*T;J.style[O]=T+P}if(Q[1]){N=((Q[1]=="-="?-1:1)*N)+T}R.custom(T,N,P)}else{R.custom(T,S,"")}}});return true})},stop:function(F,E){var G=o.timers;if(F){this.queue([])}this.each(function(){for(var H=G.length-1;H>=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J<K.length;J++){if(!K[J]()){K.splice(J--,1)}}if(!K.length){clearInterval(n);n=g}},13)}},show:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.show=true;this.custom(this.prop=="width"||this.prop=="height"?1:0,this.cur());o(this.elem).show()},hide:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(H){var G=e();if(H||G>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})();
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/data/ui.all.css	Wed Jun 03 09:15:20 2009 +0200
@@ -0,0 +1,404 @@
+/*
+* jQuery UI CSS Framework
+* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+*/
+
+/* Layout helpers
+----------------------------------*/
+.ui-helper-hidden { display: none; }
+.ui-helper-hidden-accessible { position: absolute; left: -99999999px; }
+.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
+.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+.ui-helper-clearfix { display: inline-block; }
+/* required comment for clearfix to work in Opera \*/
+* html .ui-helper-clearfix { height:1%; }
+.ui-helper-clearfix { display:block; }
+/* end clearfix */
+.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
+
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-disabled { cursor: default !important; }
+
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Overlays */
+.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
+
+/*
+* jQuery UI CSS Framework
+* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=03_highlight_soft.png&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=02_glass.png&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
+*/
+
+
+/* Component containers
+----------------------------------*/
+.ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1.1em; }
+.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
+.ui-widget-content { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x; color: #222222; }
+.ui-widget-content a { color: #222222; }
+.ui-widget-header { border: 1px solid #aaaaaa; background: #cccccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x; color: #222222; font-weight: bold; }
+.ui-widget-header a { color: #222222; }
+
+/* Interaction states
+----------------------------------*/
+.ui-state-default, .ui-widget-content .ui-state-default { border: 1px solid #d3d3d3; background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #555555; outline: none; }
+.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555; text-decoration: none; outline: none; }
+.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus { border: 1px solid #999999; background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; outline: none; }
+.ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decoration: none; outline: none; }
+.ui-state-active, .ui-widget-content .ui-state-active { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; outline: none; }
+.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121; outline: none; text-decoration: none; }
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-highlight, .ui-widget-content .ui-state-highlight {border: 1px solid #fcefa1; background: #fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x; color: #363636; }
+.ui-state-highlight a, .ui-widget-content .ui-state-highlight a { color: #363636; }
+.ui-state-error, .ui-widget-content .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; color: #cd0a0a; }
+.ui-state-error a, .ui-widget-content .ui-state-error a { color: #cd0a0a; }
+.ui-state-error-text, .ui-widget-content .ui-state-error-text { color: #cd0a0a; }
+.ui-state-disabled, .ui-widget-content .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
+.ui-priority-primary, .ui-widget-content .ui-priority-primary { font-weight: bold; }
+.ui-priority-secondary, .ui-widget-content .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); }
+.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
+.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
+.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png); }
+.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); }
+.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); }
+.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); }
+.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); }
+
+/* positioning */
+.ui-icon-carat-1-n { background-position: 0 0; }
+.ui-icon-carat-1-ne { background-position: -16px 0; }
+.ui-icon-carat-1-e { background-position: -32px 0; }
+.ui-icon-carat-1-se { background-position: -48px 0; }
+.ui-icon-carat-1-s { background-position: -64px 0; }
+.ui-icon-carat-1-sw { background-position: -80px 0; }
+.ui-icon-carat-1-w { background-position: -96px 0; }
+.ui-icon-carat-1-nw { background-position: -112px 0; }
+.ui-icon-carat-2-n-s { background-position: -128px 0; }
+.ui-icon-carat-2-e-w { background-position: -144px 0; }
+.ui-icon-triangle-1-n { background-position: 0 -16px; }
+.ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.ui-icon-triangle-1-e { background-position: -32px -16px; }
+.ui-icon-triangle-1-se { background-position: -48px -16px; }
+.ui-icon-triangle-1-s { background-position: -64px -16px; }
+.ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.ui-icon-triangle-1-w { background-position: -96px -16px; }
+.ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.ui-icon-arrow-1-n { background-position: 0 -32px; }
+.ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.ui-icon-arrow-1-e { background-position: -32px -32px; }
+.ui-icon-arrow-1-se { background-position: -48px -32px; }
+.ui-icon-arrow-1-s { background-position: -64px -32px; }
+.ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.ui-icon-arrow-1-w { background-position: -96px -32px; }
+.ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
+.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.ui-icon-arrow-4 { background-position: 0 -80px; }
+.ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.ui-icon-extlink { background-position: -32px -80px; }
+.ui-icon-newwin { background-position: -48px -80px; }
+.ui-icon-refresh { background-position: -64px -80px; }
+.ui-icon-shuffle { background-position: -80px -80px; }
+.ui-icon-transfer-e-w { background-position: -96px -80px; }
+.ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.ui-icon-folder-collapsed { background-position: 0 -96px; }
+.ui-icon-folder-open { background-position: -16px -96px; }
+.ui-icon-document { background-position: -32px -96px; }
+.ui-icon-document-b { background-position: -48px -96px; }
+.ui-icon-note { background-position: -64px -96px; }
+.ui-icon-mail-closed { background-position: -80px -96px; }
+.ui-icon-mail-open { background-position: -96px -96px; }
+.ui-icon-suitcase { background-position: -112px -96px; }
+.ui-icon-comment { background-position: -128px -96px; }
+.ui-icon-person { background-position: -144px -96px; }
+.ui-icon-print { background-position: -160px -96px; }
+.ui-icon-trash { background-position: -176px -96px; }
+.ui-icon-locked { background-position: -192px -96px; }
+.ui-icon-unlocked { background-position: -208px -96px; }
+.ui-icon-bookmark { background-position: -224px -96px; }
+.ui-icon-tag { background-position: -240px -96px; }
+.ui-icon-home { background-position: 0 -112px; }
+.ui-icon-flag { background-position: -16px -112px; }
+.ui-icon-calendar { background-position: -32px -112px; }
+.ui-icon-cart { background-position: -48px -112px; }
+.ui-icon-pencil { background-position: -64px -112px; }
+.ui-icon-clock { background-position: -80px -112px; }
+.ui-icon-disk { background-position: -96px -112px; }
+.ui-icon-calculator { background-position: -112px -112px; }
+.ui-icon-zoomin { background-position: -128px -112px; }
+.ui-icon-zoomout { background-position: -144px -112px; }
+.ui-icon-search { background-position: -160px -112px; }
+.ui-icon-wrench { background-position: -176px -112px; }
+.ui-icon-gear { background-position: -192px -112px; }
+.ui-icon-heart { background-position: -208px -112px; }
+.ui-icon-star { background-position: -224px -112px; }
+.ui-icon-link { background-position: -240px -112px; }
+.ui-icon-cancel { background-position: 0 -128px; }
+.ui-icon-plus { background-position: -16px -128px; }
+.ui-icon-plusthick { background-position: -32px -128px; }
+.ui-icon-minus { background-position: -48px -128px; }
+.ui-icon-minusthick { background-position: -64px -128px; }
+.ui-icon-close { background-position: -80px -128px; }
+.ui-icon-closethick { background-position: -96px -128px; }
+.ui-icon-key { background-position: -112px -128px; }
+.ui-icon-lightbulb { background-position: -128px -128px; }
+.ui-icon-scissors { background-position: -144px -128px; }
+.ui-icon-clipboard { background-position: -160px -128px; }
+.ui-icon-copy { background-position: -176px -128px; }
+.ui-icon-contact { background-position: -192px -128px; }
+.ui-icon-image { background-position: -208px -128px; }
+.ui-icon-video { background-position: -224px -128px; }
+.ui-icon-script { background-position: -240px -128px; }
+.ui-icon-alert { background-position: 0 -144px; }
+.ui-icon-info { background-position: -16px -144px; }
+.ui-icon-notice { background-position: -32px -144px; }
+.ui-icon-help { background-position: -48px -144px; }
+.ui-icon-check { background-position: -64px -144px; }
+.ui-icon-bullet { background-position: -80px -144px; }
+.ui-icon-radio-off { background-position: -96px -144px; }
+.ui-icon-radio-on { background-position: -112px -144px; }
+.ui-icon-pin-w { background-position: -128px -144px; }
+.ui-icon-pin-s { background-position: -144px -144px; }
+.ui-icon-play { background-position: 0 -160px; }
+.ui-icon-pause { background-position: -16px -160px; }
+.ui-icon-seek-next { background-position: -32px -160px; }
+.ui-icon-seek-prev { background-position: -48px -160px; }
+.ui-icon-seek-end { background-position: -64px -160px; }
+.ui-icon-seek-first { background-position: -80px -160px; }
+.ui-icon-stop { background-position: -96px -160px; }
+.ui-icon-eject { background-position: -112px -160px; }
+.ui-icon-volume-off { background-position: -128px -160px; }
+.ui-icon-volume-on { background-position: -144px -160px; }
+.ui-icon-power { background-position: 0 -176px; }
+.ui-icon-signal-diag { background-position: -16px -176px; }
+.ui-icon-signal { background-position: -32px -176px; }
+.ui-icon-battery-0 { background-position: -48px -176px; }
+.ui-icon-battery-1 { background-position: -64px -176px; }
+.ui-icon-battery-2 { background-position: -80px -176px; }
+.ui-icon-battery-3 { background-position: -96px -176px; }
+.ui-icon-circle-plus { background-position: 0 -192px; }
+.ui-icon-circle-minus { background-position: -16px -192px; }
+.ui-icon-circle-close { background-position: -32px -192px; }
+.ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.ui-icon-circle-zoomin { background-position: -176px -192px; }
+.ui-icon-circle-zoomout { background-position: -192px -192px; }
+.ui-icon-circle-check { background-position: -208px -192px; }
+.ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.ui-icon-circlesmall-close { background-position: -32px -208px; }
+.ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.ui-icon-squaresmall-close { background-position: -80px -208px; }
+.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; }
+.ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; }
+.ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; }
+.ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; }
+.ui-corner-top { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; }
+.ui-corner-bottom { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; }
+.ui-corner-right {  -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; }
+.ui-corner-left { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; }
+.ui-corner-all { -moz-border-radius: 4px; -webkit-border-radius: 4px; }
+
+/* Overlays */
+.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); }
+.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; }/* Accordion
+----------------------------------*/
+.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
+.ui-accordion .ui-accordion-li-fix { display: inline; }
+.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
+.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em 2.2em; }
+.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
+.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; }
+.ui-accordion .ui-accordion-content-active { display: block; }/* Datepicker
+----------------------------------*/
+.ui-datepicker { width: 17em; padding: .2em .2em 0; }
+.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
+.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
+.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
+.ui-datepicker .ui-datepicker-prev { left:2px; }
+.ui-datepicker .ui-datepicker-next { right:2px; }
+.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
+.ui-datepicker .ui-datepicker-next-hover { right:1px; }
+.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px;  }
+.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
+.ui-datepicker .ui-datepicker-title select { float:left; font-size:1em; margin:1px 0; }
+.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
+.ui-datepicker select.ui-datepicker-month, 
+.ui-datepicker select.ui-datepicker-year { width: 49%;}
+.ui-datepicker .ui-datepicker-title select.ui-datepicker-year { float: right; }
+.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
+.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0;  }
+.ui-datepicker td { border: 0; padding: 1px; }
+.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
+.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
+.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
+.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
+
+/* with multiple calendars */
+.ui-datepicker.ui-datepicker-multi { width:auto; }
+.ui-datepicker-multi .ui-datepicker-group { float:left; }
+.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
+.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
+.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
+.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
+.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
+.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
+.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
+.ui-datepicker-row-break { clear:both; width:100%; }
+
+/* RTL support */
+.ui-datepicker-rtl { direction: rtl; }
+.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+
+/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
+.ui-datepicker-cover {
+    display: none; /*sorry for IE5*/
+    display/**/: block; /*sorry for IE5*/
+    position: absolute; /*must have*/
+    z-index: -1; /*must have*/
+    filter: mask(); /*must have*/
+    top: -4px; /*must have*/
+    left: -4px; /*must have*/
+    width: 200px; /*must have*/
+    height: 200px; /*must have*/
+}/* Dialog
+----------------------------------*/
+.ui-dialog { position: relative; padding: .2em; width: 300px; }
+.ui-dialog .ui-dialog-titlebar { padding: .5em .3em .3em 1em; position: relative;  }
+.ui-dialog .ui-dialog-title { float: left; margin: .1em 0 .2em; } 
+.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
+.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
+.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
+.ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
+.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
+.ui-dialog .ui-dialog-buttonpane button { float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; }
+.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
+.ui-draggable .ui-dialog-titlebar { cursor: move; }
+/* Progressbar
+----------------------------------*/
+.ui-progressbar { height:2em; text-align: left; }
+.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }/* Resizable
+----------------------------------*/
+.ui-resizable { position: relative;}
+.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;}
+.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
+.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0px; }
+.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0px; }
+.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0px; height: 100%; }
+.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0px; height: 100%; }
+.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
+.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
+.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
+.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* Slider
+----------------------------------*/
+.ui-slider { position: relative; text-align: left; }
+.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
+.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; }
+
+.ui-slider-horizontal { height: .8em; }
+.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
+.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
+.ui-slider-horizontal .ui-slider-range-min { left: 0; }
+.ui-slider-horizontal .ui-slider-range-max { right: 0; }
+
+.ui-slider-vertical { width: .8em; height: 100px; }
+.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
+.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
+.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
+.ui-slider-vertical .ui-slider-range-max { top: 0; }/* Tabs
+----------------------------------*/
+.ui-tabs { padding: .2em; zoom: 1; }
+.ui-tabs .ui-tabs-nav { list-style: none; position: relative; padding: .2em .2em 0; }
+.ui-tabs .ui-tabs-nav li { position: relative; float: left; border-bottom-width: 0 !important; margin: 0 .2em -1px 0; padding: 0; }
+.ui-tabs .ui-tabs-nav li a { float: left; text-decoration: none; padding: .5em 1em; }
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected { padding-bottom: 1px; border-bottom-width: 0; }
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
+.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
+.ui-tabs .ui-tabs-panel { padding: 1em 1.4em; display: block; border-width: 0; background: none; }
+.ui-tabs .ui-tabs-hide { display: none !important; }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/data/ui.slider.js	Wed Jun 03 09:15:20 2009 +0200
@@ -0,0 +1,43 @@
+/*
+ * jQuery UI 1.7.1
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI
+ */
jQuery.ui||(function(c){var i=c.fn.remove,d=c.browser.mozilla&&(parseFloat(c.browser.version)<1.9);c.ui={version:"1.7.1",plugin:{add:function(k,l,n){var m=c.ui[k].prototype;for(var j in n){m.plugins[j]=m.plugins[j]||[];m.plugins[j].push([l,n[j]])}},call:function(j,l,k){var n=j.plugins[l];if(!n||!j.element[0].parentNode){return}for(var m=0;m<n.length;m++){if(j.options[n[m][0]]){n[m][1].apply(j.element,k)}}}},contains:function(k,j){return document.compareDocumentPosition?k.compareDocumentPosition(j)&16:k!==j&&k.contains(j)},hasScroll:function(m,k){if(c(m).css("overflow")=="hidden"){return false}var j=(k&&k=="left")?"scrollLeft":"scrollTop",l=false;if(m[j]>0){return true}m[j]=1;l=(m[j]>0);m[j]=0;return l},isOverAxis:function(k,j,l){return(k>j)&&(k<(j+l))},isOver:function(o,k,n,m,j,l){return c.ui.isOverAxis(o,n,j)&&c.ui.isOverAxis(k,m,l)},keyCode:{BACKSPACE:8,CAPS_LOCK:20,COMMA:188,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38}};if(d){var f=c.attr,e=c.fn.removeAttr,h="http://www.w3.org/2005/07/aaa",a=/^aria-/,b=/^wairole:/;c.attr=function(k,j,l){var m=l!==undefined;return(j=="role"?(m?f.call(this,k,j,"wairole:"+l):(f.apply(this,arguments)||"").replace(b,"")):(a.test(j)?(m?k.setAttributeNS(h,j.replace(a,"aaa:"),l):f.call(this,k,j.replace(a,"aaa:"))):f.apply(this,arguments)))};c.fn.removeAttr=function(j){return(a.test(j)?this.each(function(){this.removeAttributeNS(h,j.replace(a,""))}):e.call(this,j))}}c.fn.extend({remove:function(){c("*",this).add(this).each(function(){c(this).triggerHandler("remove")});return i.apply(this,arguments)},enableSelection:function(){return this.attr("unselectable","off").css("MozUserSelect","").unbind("selectstart.ui")},disableSelection:function(){return this.attr("unselectable","on").css("MozUserSelect","none").bind("selectstart.ui",function(){return false})},scrollParent:function(){var j;if((c.browser.msie&&(/(static|relative)/).test(this.css("position")))||(/absolute/).test(this.css("position"))){j=this.parents().filter(function(){return(/(relative|absolute|fixed)/).test(c.curCSS(this,"position",1))&&(/(auto|scroll)/).test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0)}else{j=this.parents().filter(function(){return(/(auto|scroll)/).test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0)}return(/fixed/).test(this.css("position"))||!j.length?c(document):j}});c.extend(c.expr[":"],{data:function(l,k,j){return !!c.data(l,j[3])},focusable:function(k){var l=k.nodeName.toLowerCase(),j=c.attr(k,"tabindex");return(/input|select|textarea|button|object/.test(l)?!k.disabled:"a"==l||"area"==l?k.href||!isNaN(j):!isNaN(j))&&!c(k)["area"==l?"parents":"closest"](":hidden").length},tabbable:function(k){var j=c.attr(k,"tabindex");return(isNaN(j)||j>=0)&&c(k).is(":focusable")}});function g(m,n,o,l){function k(q){var p=c[m][n][q]||[];return(typeof p=="string"?p.split(/,?\s+/):p)}var j=k("getter");if(l.length==1&&typeof l[0]=="string"){j=j.concat(k("getterSetter"))}return(c.inArray(o,j)!=-1)}c.widget=function(k,j){var l=k.split(".")[0];k=k.split(".")[1];c.fn[k]=function(p){var n=(typeof p=="string"),o=Array.prototype.slice.call(arguments,1);if(n&&p.substring(0,1)=="_"){return this}if(n&&g(l,k,p,o)){var m=c.data(this[0],k);return(m?m[p].apply(m,o):undefined)}return this.each(function(){var q=c.data(this,k);(!q&&!n&&c.data(this,k,new c[l][k](this,p))._init());(q&&n&&c.isFunction(q[p])&&q[p].apply(q,o))})};c[l]=c[l]||{};c[l][k]=function(o,n){var m=this;this.namespace=l;this.widgetName=k;this.widgetEventPrefix=c[l][k].eventPrefix||k;this.widgetBaseClass=l+"-"+k;this.options=c.extend({},c.widget.defaults,c[l][k].defaults,c.metadata&&c.metadata.get(o)[k],n);this.element=c(o).bind("setData."+k,function(q,p,r){if(q.target==o){return m._setData(p,r)}}).bind("getData."+k,function(q,p){if(q.target==o){return m._getData(p)}}).bind("remove",function(){return m.destroy()})};c[l][k].prototype=c.extend({},c.widget.prototype,j);c[l][k].getterSetter="option"};c.widget.prototype={_init:function(){},destroy:function(){this.element.removeData(this.widgetName).removeClass(this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").removeAttr("aria-disabled")},option:function(l,m){var k=l,j=this;if(typeof l=="string"){if(m===undefined){return this._getData(l)}k={};k[l]=m}c.each(k,function(n,o){j._setData(n,o)})},_getData:function(j){return this.options[j]},_setData:function(j,k){this.options[j]=k;if(j=="disabled"){this.element[k?"addClass":"removeClass"](this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").attr("aria-disabled",k)}},enable:function(){this._setData("disabled",false)},disable:function(){this._setData("disabled",true)},_trigger:function(l,m,n){var p=this.options[l],j=(l==this.widgetEventPrefix?l:this.widgetEventPrefix+l);m=c.Event(m);m.type=j;if(m.originalEvent){for(var k=c.event.props.length,o;k;){o=c.event.props[--k];m[o]=m.originalEvent[o]}}this.element.trigger(m,n);return !(c.isFunction(p)&&p.call(this.element[0],m,n)===false||m.isDefaultPrevented())}};c.widget.defaults={disabled:false};c.ui.mouse={_mouseInit:function(){var j=this;this.element.bind("mousedown."+this.widgetName,function(k){return j._mouseDown(k)}).bind("click."+this.widgetName,function(k){if(j._preventClickEvent){j._preventClickEvent=false;k.stopImmediatePropagation();return false}});if(c.browser.msie){this._mouseUnselectable=this.element.attr("unselectable");this.element.attr("unselectable","on")}this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName);(c.browser.msie&&this.element.attr("unselectable",this._mouseUnselectable))},_mouseDown:function(l){l.originalEvent=l.originalEvent||{};if(l.originalEvent.mouseHandled){return}(this._mouseStarted&&this._mouseUp(l));this._mouseDownEvent=l;var k=this,m=(l.which==1),j=(typeof this.options.cancel=="string"?c(l.target).parents().add(l.target).filter(this.options.cancel).length:false);if(!m||j||!this._mouseCapture(l)){return true}this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){k.mouseDelayMet=true},this.options.delay)}if(this._mouseDistanceMet(l)&&this._mouseDelayMet(l)){this._mouseStarted=(this._mouseStart(l)!==false);if(!this._mouseStarted){l.preventDefault();return true}}this._mouseMoveDelegate=function(n){return k._mouseMove(n)};this._mouseUpDelegate=function(n){return k._mouseUp(n)};c(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);(c.browser.safari||l.preventDefault());l.originalEvent.mouseHandled=true;return true},_mouseMove:function(j){if(c.browser.msie&&!j.button){return this._mouseUp(j)}if(this._mouseStarted){this._mouseDrag(j);return j.preventDefault()}if(this._mouseDistanceMet(j)&&this._mouseDelayMet(j)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,j)!==false);(this._mouseStarted?this._mouseDrag(j):this._mouseUp(j))}return !this._mouseStarted},_mouseUp:function(j){c(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._preventClickEvent=(j.target==this._mouseDownEvent.target);this._mouseStop(j)}return false},_mouseDistanceMet:function(j){return(Math.max(Math.abs(this._mouseDownEvent.pageX-j.pageX),Math.abs(this._mouseDownEvent.pageY-j.pageY))>=this.options.distance)},_mouseDelayMet:function(j){return this.mouseDelayMet},_mouseStart:function(j){},_mouseDrag:function(j){},_mouseStop:function(j){},_mouseCapture:function(j){return true}};c.ui.mouse.defaults={cancel:null,distance:1,delay:0}})(jQuery);;/*
+ * jQuery UI Draggable 1.7.1
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Draggables
+ *
+ * Depends:
+ *	ui.core.js
+ */
(function(a){a.widget("ui.draggable",a.extend({},a.ui.mouse,{_init:function(){if(this.options.helper=="original"&&!(/^(?:r|a|f)/).test(this.element.css("position"))){this.element[0].style.position="relative"}(this.options.addClasses&&this.element.addClass("ui-draggable"));(this.options.disabled&&this.element.addClass("ui-draggable-disabled"));this._mouseInit()},destroy:function(){if(!this.element.data("draggable")){return}this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy()},_mouseCapture:function(b){var c=this.options;if(this.helper||c.disabled||a(b.target).is(".ui-resizable-handle")){return false}this.handle=this._getHandle(b);if(!this.handle){return false}return true},_mouseStart:function(b){var c=this.options;this.helper=this._createHelper(b);this._cacheHelperProportions();if(a.ui.ddmanager){a.ui.ddmanager.current=this}this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};a.extend(this.offset,{click:{left:b.pageX-this.offset.left,top:b.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(b);this.originalPageX=b.pageX;this.originalPageY=b.pageY;if(c.cursorAt){this._adjustOffsetFromHelper(c.cursorAt)}if(c.containment){this._setContainment()}this._trigger("start",b);this._cacheHelperProportions();if(a.ui.ddmanager&&!c.dropBehaviour){a.ui.ddmanager.prepareOffsets(this,b)}this.helper.addClass("ui-draggable-dragging");this._mouseDrag(b,true);return true},_mouseDrag:function(b,d){this.position=this._generatePosition(b);this.positionAbs=this._convertPositionTo("absolute");if(!d){var c=this._uiHash();this._trigger("drag",b,c);this.position=c.position}if(!this.options.axis||this.options.axis!="y"){this.helper[0].style.left=this.position.left+"px"}if(!this.options.axis||this.options.axis!="x"){this.helper[0].style.top=this.position.top+"px"}if(a.ui.ddmanager){a.ui.ddmanager.drag(this,b)}return false},_mouseStop:function(c){var d=false;if(a.ui.ddmanager&&!this.options.dropBehaviour){d=a.ui.ddmanager.drop(this,c)}if(this.dropped){d=this.dropped;this.dropped=false}if((this.options.revert=="invalid"&&!d)||(this.options.revert=="valid"&&d)||this.options.revert===true||(a.isFunction(this.options.revert)&&this.options.revert.call(this.element,d))){var b=this;a(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){b._trigger("stop",c);b._clear()})}else{this._trigger("stop",c);this._clear()}return false},_getHandle:function(b){var c=!this.options.handle||!a(this.options.handle,this.element).length?true:false;a(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==b.target){c=true}});return c},_createHelper:function(c){var d=this.options;var b=a.isFunction(d.helper)?a(d.helper.apply(this.element[0],[c])):(d.helper=="clone"?this.element.clone():this.element);if(!b.parents("body").length){b.appendTo((d.appendTo=="parent"?this.element[0].parentNode:d.appendTo))}if(b[0]!=this.element[0]&&!(/(fixed|absolute)/).test(b.css("position"))){b.css("position","absolute")}return b},_adjustOffsetFromHelper:function(b){if(b.left!=undefined){this.offset.click.left=b.left+this.margins.left}if(b.right!=undefined){this.offset.click.left=this.helperProportions.width-b.right+this.margins.left}if(b.top!=undefined){this.offset.click.top=b.top+this.margins.top}if(b.bottom!=undefined){this.offset.click.top=this.helperProportions.height-b.bottom+this.margins.top}},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var b=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0])){b.left+=this.scrollParent.scrollLeft();b.top+=this.scrollParent.scrollTop()}if((this.offsetParent[0]==document.body)||(this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&a.browser.msie)){b={top:0,left:0}}return{top:b.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:b.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var b=this.element.position();return{top:b.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:b.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else{return{top:0,left:0}}},_cacheMargins:function(){this.margins={left:(parseInt(this.element.css("marginLeft"),10)||0),top:(parseInt(this.element.css("marginTop"),10)||0)}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e=this.options;if(e.containment=="parent"){e.containment=this.helper[0].parentNode}if(e.containment=="document"||e.containment=="window"){this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,a(e.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a(e.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]}if(!(/^(document|window|parent)$/).test(e.containment)&&e.containment.constructor!=Array){var c=a(e.containment)[0];if(!c){return}var d=a(e.containment).offset();var b=(a(c).css("overflow")!="hidden");this.containment=[d.left+(parseInt(a(c).css("borderLeftWidth"),10)||0)+(parseInt(a(c).css("paddingLeft"),10)||0)-this.margins.left,d.top+(parseInt(a(c).css("borderTopWidth"),10)||0)+(parseInt(a(c).css("paddingTop"),10)||0)-this.margins.top,d.left+(b?Math.max(c.scrollWidth,c.offsetWidth):c.offsetWidth)-(parseInt(a(c).css("borderLeftWidth"),10)||0)-(parseInt(a(c).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,d.top+(b?Math.max(c.scrollHeight,c.offsetHeight):c.offsetHeight)-(parseInt(a(c).css("borderTopWidth"),10)||0)-(parseInt(a(c).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}else{if(e.containment.constructor==Array){this.containment=e.containment}}},_convertPositionTo:function(f,h){if(!h){h=this.position}var c=f=="absolute"?1:-1;var e=this.options,b=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,g=(/(html|body)/i).test(b[0].tagName);return{top:(h.top+this.offset.relative.top*c+this.offset.parent.top*c-(a.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():(g?0:b.scrollTop()))*c)),left:(h.left+this.offset.relative.left*c+this.offset.parent.left*c-(a.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():g?0:b.scrollLeft())*c))}},_generatePosition:function(e){var h=this.options,b=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,i=(/(html|body)/i).test(b[0].tagName);if(this.cssPosition=="relative"&&!(this.scrollParent[0]!=document&&this.scrollParent[0]!=this.offsetParent[0])){this.offset.relative=this._getRelativeOffset()}var d=e.pageX;var c=e.pageY;if(this.originalPosition){if(this.containment){if(e.pageX-this.offset.click.left<this.containment[0]){d=this.containment[0]+this.offset.click.left}if(e.pageY-this.offset.click.top<this.containment[1]){c=this.containment[1]+this.offset.click.top}if(e.pageX-this.offset.click.left>this.containment[2]){d=this.containment[2]+this.offset.click.left}if(e.pageY-this.offset.click.top>this.containment[3]){c=this.containment[3]+this.offset.click.top}}if(h.grid){var g=this.originalPageY+Math.round((c-this.originalPageY)/h.grid[1])*h.grid[1];c=this.containment?(!(g-this.offset.click.top<this.containment[1]||g-this.offset.click.top>this.containment[3])?g:(!(g-this.offset.click.top<this.containment[1])?g-h.grid[1]:g+h.grid[1])):g;var f=this.originalPageX+Math.round((d-this.originalPageX)/h.grid[0])*h.grid[0];d=this.containment?(!(f-this.offset.click.left<this.containment[0]||f-this.offset.click.left>this.containment[2])?f:(!(f-this.offset.click.left<this.containment[0])?f-h.grid[0]:f+h.grid[0])):f}}return{top:(c-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(a.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():(i?0:b.scrollTop())))),left:(d-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(a.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():i?0:b.scrollLeft())))}},_clear:function(){this.helper.removeClass("ui-draggable-dragging");if(this.helper[0]!=this.element[0]&&!this.cancelHelperRemoval){this.helper.remove()}this.helper=null;this.cancelHelperRemoval=false},_trigger:function(b,c,d){d=d||this._uiHash();a.ui.plugin.call(this,b,[c,d]);if(b=="drag"){this.positionAbs=this._convertPositionTo("absolute")}return a.widget.prototype._trigger.call(this,b,c,d)},plugins:{},_uiHash:function(b){return{helper:this.helper,position:this.position,absolutePosition:this.positionAbs,offset:this.positionAbs}}}));a.extend(a.ui.draggable,{version:"1.7.1",eventPrefix:"drag",defaults:{addClasses:true,appendTo:"parent",axis:false,cancel:":input,option",connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,delay:0,distance:1,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false}});a.ui.plugin.add("draggable","connectToSortable",{start:function(c,e){var d=a(this).data("draggable"),f=d.options,b=a.extend({},e,{item:d.element});d.sortables=[];a(f.connectToSortable).each(function(){var g=a.data(this,"sortable");if(g&&!g.options.disabled){d.sortables.push({instance:g,shouldRevert:g.options.revert});g._refreshItems();g._trigger("activate",c,b)}})},stop:function(c,e){var d=a(this).data("draggable"),b=a.extend({},e,{item:d.element});a.each(d.sortables,function(){if(this.instance.isOver){this.instance.isOver=0;d.cancelHelperRemoval=true;this.instance.cancelHelperRemoval=false;if(this.shouldRevert){this.instance.options.revert=true}this.instance._mouseStop(c);this.instance.options.helper=this.instance.options._helper;if(d.options.helper=="original"){this.instance.currentItem.css({top:"auto",left:"auto"})}}else{this.instance.cancelHelperRemoval=false;this.instance._trigger("deactivate",c,b)}})},drag:function(c,f){var e=a(this).data("draggable"),b=this;var d=function(i){var n=this.offset.click.top,m=this.offset.click.left;var g=this.positionAbs.top,k=this.positionAbs.left;var j=i.height,l=i.width;var p=i.top,h=i.left;return a.ui.isOver(g+n,k+m,p,h,j,l)};a.each(e.sortables,function(g){this.instance.positionAbs=e.positionAbs;this.instance.helperProportions=e.helperProportions;this.instance.offset.click=e.offset.click;if(this.instance._intersectsWith(this.instance.containerCache)){if(!this.instance.isOver){this.instance.isOver=1;this.instance.currentItem=a(b).clone().appendTo(this.instance.element).data("sortable-item",true);this.instance.options._helper=this.instance.options.helper;this.instance.options.helper=function(){return f.helper[0]};c.target=this.instance.currentItem[0];this.instance._mouseCapture(c,true);this.instance._mouseStart(c,true,true);this.instance.offset.click.top=e.offset.click.top;this.instance.offset.click.left=e.offset.click.left;this.instance.offset.parent.left-=e.offset.parent.left-this.instance.offset.parent.left;this.instance.offset.parent.top-=e.offset.parent.top-this.instance.offset.parent.top;e._trigger("toSortable",c);e.dropped=this.instance.element;e.currentItem=e.element;this.instance.fromOutside=e}if(this.instance.currentItem){this.instance._mouseDrag(c)}}else{if(this.instance.isOver){this.instance.isOver=0;this.instance.cancelHelperRemoval=true;this.instance.options.revert=false;this.instance._trigger("out",c,this.instance._uiHash(this.instance));this.instance._mouseStop(c,true);this.instance.options.helper=this.instance.options._helper;this.instance.currentItem.remove();if(this.instance.placeholder){this.instance.placeholder.remove()}e._trigger("fromSortable",c);e.dropped=false}}})}});a.ui.plugin.add("draggable","cursor",{start:function(c,d){var b=a("body"),e=a(this).data("draggable").options;if(b.css("cursor")){e._cursor=b.css("cursor")}b.css("cursor",e.cursor)},stop:function(b,c){var d=a(this).data("draggable").options;if(d._cursor){a("body").css("cursor",d._cursor)}}});a.ui.plugin.add("draggable","iframeFix",{start:function(b,c){var d=a(this).data("draggable").options;a(d.iframeFix===true?"iframe":d.iframeFix).each(function(){a('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1000}).css(a(this).offset()).appendTo("body")})},stop:function(b,c){a("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)})}});a.ui.plugin.add("draggable","opacity",{start:function(c,d){var b=a(d.helper),e=a(this).data("draggable").options;if(b.css("opacity")){e._opacity=b.css("opacity")}b.css("opacity",e.opacity)},stop:function(b,c){var d=a(this).data("draggable").options;if(d._opacity){a(c.helper).css("opacity",d._opacity)}}});a.ui.plugin.add("draggable","scroll",{start:function(c,d){var b=a(this).data("draggable");if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!="HTML"){b.overflowOffset=b.scrollParent.offset()}},drag:function(d,e){var c=a(this).data("draggable"),f=c.options,b=false;if(c.scrollParent[0]!=document&&c.scrollParent[0].tagName!="HTML"){if(!f.axis||f.axis!="x"){if((c.overflowOffset.top+c.scrollParent[0].offsetHeight)-d.pageY<f.scrollSensitivity){c.scrollParent[0].scrollTop=b=c.scrollParent[0].scrollTop+f.scrollSpeed}else{if(d.pageY-c.overflowOffset.top<f.scrollSensitivity){c.scrollParent[0].scrollTop=b=c.scrollParent[0].scrollTop-f.scrollSpeed}}}if(!f.axis||f.axis!="y"){if((c.overflowOffset.left+c.scrollParent[0].offsetWidth)-d.pageX<f.scrollSensitivity){c.scrollParent[0].scrollLeft=b=c.scrollParent[0].scrollLeft+f.scrollSpeed}else{if(d.pageX-c.overflowOffset.left<f.scrollSensitivity){c.scrollParent[0].scrollLeft=b=c.scrollParent[0].scrollLeft-f.scrollSpeed}}}}else{if(!f.axis||f.axis!="x"){if(d.pageY-a(document).scrollTop()<f.scrollSensitivity){b=a(document).scrollTop(a(document).scrollTop()-f.scrollSpeed)}else{if(a(window).height()-(d.pageY-a(document).scrollTop())<f.scrollSensitivity){b=a(document).scrollTop(a(document).scrollTop()+f.scrollSpeed)}}}if(!f.axis||f.axis!="y"){if(d.pageX-a(document).scrollLeft()<f.scrollSensitivity){b=a(document).scrollLeft(a(document).scrollLeft()-f.scrollSpeed)}else{if(a(window).width()-(d.pageX-a(document).scrollLeft())<f.scrollSensitivity){b=a(document).scrollLeft(a(document).scrollLeft()+f.scrollSpeed)}}}}if(b!==false&&a.ui.ddmanager&&!f.dropBehaviour){a.ui.ddmanager.prepareOffsets(c,d)}}});a.ui.plugin.add("draggable","snap",{start:function(c,d){var b=a(this).data("draggable"),e=b.options;b.snapElements=[];a(e.snap.constructor!=String?(e.snap.items||":data(draggable)"):e.snap).each(function(){var g=a(this);var f=g.offset();if(this!=b.element[0]){b.snapElements.push({item:this,width:g.outerWidth(),height:g.outerHeight(),top:f.top,left:f.left})}})},drag:function(u,p){var g=a(this).data("draggable"),q=g.options;var y=q.snapTolerance;var x=p.offset.left,w=x+g.helperProportions.width,f=p.offset.top,e=f+g.helperProportions.height;for(var v=g.snapElements.length-1;v>=0;v--){var s=g.snapElements[v].left,n=s+g.snapElements[v].width,m=g.snapElements[v].top,A=m+g.snapElements[v].height;if(!((s-y<x&&x<n+y&&m-y<f&&f<A+y)||(s-y<x&&x<n+y&&m-y<e&&e<A+y)||(s-y<w&&w<n+y&&m-y<f&&f<A+y)||(s-y<w&&w<n+y&&m-y<e&&e<A+y))){if(g.snapElements[v].snapping){(g.options.snap.release&&g.options.snap.release.call(g.element,u,a.extend(g._uiHash(),{snapItem:g.snapElements[v].item})))}g.snapElements[v].snapping=false;continue}if(q.snapMode!="inner"){var c=Math.abs(m-e)<=y;var z=Math.abs(A-f)<=y;var j=Math.abs(s-w)<=y;var k=Math.abs(n-x)<=y;if(c){p.position.top=g._convertPositionTo("relative",{top:m-g.helperProportions.height,left:0}).top-g.margins.top}if(z){p.position.top=g._convertPositionTo("relative",{top:A,left:0}).top-g.margins.top}if(j){p.position.left=g._convertPositionTo("relative",{top:0,left:s-g.helperProportions.width}).left-g.margins.left}if(k){p.position.left=g._convertPositionTo("relative",{top:0,left:n}).left-g.margins.left}}var h=(c||z||j||k);if(q.snapMode!="outer"){var c=Math.abs(m-f)<=y;var z=Math.abs(A-e)<=y;var j=Math.abs(s-x)<=y;var k=Math.abs(n-w)<=y;if(c){p.position.top=g._convertPositionTo("relative",{top:m,left:0}).top-g.margins.top}if(z){p.position.top=g._convertPositionTo("relative",{top:A-g.helperProportions.height,left:0}).top-g.margins.top}if(j){p.position.left=g._convertPositionTo("relative",{top:0,left:s}).left-g.margins.left}if(k){p.position.left=g._convertPositionTo("relative",{top:0,left:n-g.helperProportions.width}).left-g.margins.left}}if(!g.snapElements[v].snapping&&(c||z||j||k||h)){(g.options.snap.snap&&g.options.snap.snap.call(g.element,u,a.extend(g._uiHash(),{snapItem:g.snapElements[v].item})))}g.snapElements[v].snapping=(c||z||j||k||h)}}});a.ui.plugin.add("draggable","stack",{start:function(b,c){var e=a(this).data("draggable").options;var d=a.makeArray(a(e.stack.group)).sort(function(g,f){return(parseInt(a(g).css("zIndex"),10)||e.stack.min)-(parseInt(a(f).css("zIndex"),10)||e.stack.min)});a(d).each(function(f){this.style.zIndex=e.stack.min+f});this[0].style.zIndex=e.stack.min+d.length}});a.ui.plugin.add("draggable","zIndex",{start:function(c,d){var b=a(d.helper),e=a(this).data("draggable").options;if(b.css("zIndex")){e._zIndex=b.css("zIndex")}b.css("zIndex",e.zIndex)},stop:function(b,c){var d=a(this).data("draggable").options;if(d._zIndex){a(c.helper).css("zIndex",d._zIndex)}}})})(jQuery);;/*
+ * jQuery UI Droppable 1.7.1
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Droppables
+ *
+ * Depends:
+ *	ui.core.js
+ *	ui.draggable.js
+ */
(function(a){a.widget("ui.droppable",{_init:function(){var c=this.options,b=c.accept;this.isover=0;this.isout=1;this.options.accept=this.options.accept&&a.isFunction(this.options.accept)?this.options.accept:function(e){return e.is(b)};this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight};a.ui.ddmanager.droppables[this.options.scope]=a.ui.ddmanager.droppables[this.options.scope]||[];a.ui.ddmanager.droppables[this.options.scope].push(this);(this.options.addClasses&&this.element.addClass("ui-droppable"))},destroy:function(){var b=a.ui.ddmanager.droppables[this.options.scope];for(var c=0;c<b.length;c++){if(b[c]==this){b.splice(c,1)}}this.element.removeClass("ui-droppable ui-droppable-disabled").removeData("droppable").unbind(".droppable")},_setData:function(b,c){if(b=="accept"){this.options.accept=c&&a.isFunction(c)?c:function(e){return e.is(c)}}else{a.widget.prototype._setData.apply(this,arguments)}},_activate:function(c){var b=a.ui.ddmanager.current;if(this.options.activeClass){this.element.addClass(this.options.activeClass)}(b&&this._trigger("activate",c,this.ui(b)))},_deactivate:function(c){var b=a.ui.ddmanager.current;if(this.options.activeClass){this.element.removeClass(this.options.activeClass)}(b&&this._trigger("deactivate",c,this.ui(b)))},_over:function(c){var b=a.ui.ddmanager.current;if(!b||(b.currentItem||b.element)[0]==this.element[0]){return}if(this.options.accept.call(this.element[0],(b.currentItem||b.element))){if(this.options.hoverClass){this.element.addClass(this.options.hoverClass)}this._trigger("over",c,this.ui(b))}},_out:function(c){var b=a.ui.ddmanager.current;if(!b||(b.currentItem||b.element)[0]==this.element[0]){return}if(this.options.accept.call(this.element[0],(b.currentItem||b.element))){if(this.options.hoverClass){this.element.removeClass(this.options.hoverClass)}this._trigger("out",c,this.ui(b))}},_drop:function(c,d){var b=d||a.ui.ddmanager.current;if(!b||(b.currentItem||b.element)[0]==this.element[0]){return false}var e=false;this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var f=a.data(this,"droppable");if(f.options.greedy&&a.ui.intersect(b,a.extend(f,{offset:f.element.offset()}),f.options.tolerance)){e=true;return false}});if(e){return false}if(this.options.accept.call(this.element[0],(b.currentItem||b.element))){if(this.options.activeClass){this.element.removeClass(this.options.activeClass)}if(this.options.hoverClass){this.element.removeClass(this.options.hoverClass)}this._trigger("drop",c,this.ui(b));return this.element}return false},ui:function(b){return{draggable:(b.currentItem||b.element),helper:b.helper,position:b.position,absolutePosition:b.positionAbs,offset:b.positionAbs}}});a.extend(a.ui.droppable,{version:"1.7.1",eventPrefix:"drop",defaults:{accept:"*",activeClass:false,addClasses:true,greedy:false,hoverClass:false,scope:"default",tolerance:"intersect"}});a.ui.intersect=function(q,j,o){if(!j.offset){return false}var e=(q.positionAbs||q.position.absolute).left,d=e+q.helperProportions.width,n=(q.positionAbs||q.position.absolute).top,m=n+q.helperProportions.height;var g=j.offset.left,c=g+j.proportions.width,p=j.offset.top,k=p+j.proportions.height;switch(o){case"fit":return(g<e&&d<c&&p<n&&m<k);break;case"intersect":return(g<e+(q.helperProportions.width/2)&&d-(q.helperProportions.width/2)<c&&p<n+(q.helperProportions.height/2)&&m-(q.helperProportions.height/2)<k);break;case"pointer":var h=((q.positionAbs||q.position.absolute).left+(q.clickOffset||q.offset.click).left),i=((q.positionAbs||q.position.absolute).top+(q.clickOffset||q.offset.click).top),f=a.ui.isOver(i,h,p,g,j.proportions.height,j.proportions.width);return f;break;case"touch":return((n>=p&&n<=k)||(m>=p&&m<=k)||(n<p&&m>k))&&((e>=g&&e<=c)||(d>=g&&d<=c)||(e<g&&d>c));break;default:return false;break}};a.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(e,g){var b=a.ui.ddmanager.droppables[e.options.scope];var f=g?g.type:null;var h=(e.currentItem||e.element).find(":data(droppable)").andSelf();droppablesLoop:for(var d=0;d<b.length;d++){if(b[d].options.disabled||(e&&!b[d].options.accept.call(b[d].element[0],(e.currentItem||e.element)))){continue}for(var c=0;c<h.length;c++){if(h[c]==b[d].element[0]){b[d].proportions.height=0;continue droppablesLoop}}b[d].visible=b[d].element.css("display")!="none";if(!b[d].visible){continue}b[d].offset=b[d].element.offset();b[d].proportions={width:b[d].element[0].offsetWidth,height:b[d].element[0].offsetHeight};if(f=="mousedown"){b[d]._activate.call(b[d],g)}}},drop:function(b,c){var d=false;a.each(a.ui.ddmanager.droppables[b.options.scope],function(){if(!this.options){return}if(!this.options.disabled&&this.visible&&a.ui.intersect(b,this,this.options.tolerance)){d=this._drop.call(this,c)}if(!this.options.disabled&&this.visible&&this.options.accept.call(this.element[0],(b.currentItem||b.element))){this.isout=1;this.isover=0;this._deactivate.call(this,c)}});return d},drag:function(b,c){if(b.options.refreshPositions){a.ui.ddmanager.prepareOffsets(b,c)}a.each(a.ui.ddmanager.droppables[b.options.scope],function(){if(this.options.disabled||this.greedyChild||!this.visible){return}var e=a.ui.intersect(b,this,this.options.tolerance);var g=!e&&this.isover==1?"isout":(e&&this.isover==0?"isover":null);if(!g){return}var f;if(this.options.greedy){var d=this.element.parents(":data(droppable):eq(0)");if(d.length){f=a.data(d[0],"droppable");f.greedyChild=(g=="isover"?1:0)}}if(f&&g=="isover"){f.isover=0;f.isout=1;f._out.call(f,c)}this[g]=1;this[g=="isout"?"isover":"isout"]=0;this[g=="isover"?"_over":"_out"].call(this,c);if(f&&g=="isout"){f.isout=0;f.isover=1;f._over.call(f,c)}})}}})(jQuery);;/*
+ * jQuery UI Slider 1.7.1
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Slider
+ *
+ * Depends:
+ *	ui.core.js
+ */
(function(a){a.widget("ui.slider",a.extend({},a.ui.mouse,{_init:function(){var b=this,c=this.options;this._keySliding=false;this._handleIndex=null;this._detectOrientation();this._mouseInit();this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget ui-widget-content ui-corner-all");this.range=a([]);if(c.range){if(c.range===true){this.range=a("<div></div>");if(!c.values){c.values=[this._valueMin(),this._valueMin()]}if(c.values.length&&c.values.length!=2){c.values=[c.values[0],c.values[0]]}}else{this.range=a("<div></div>")}this.range.appendTo(this.element).addClass("ui-slider-range");if(c.range=="min"||c.range=="max"){this.range.addClass("ui-slider-range-"+c.range)}this.range.addClass("ui-widget-header")}if(a(".ui-slider-handle",this.element).length==0){a('<a href="#"></a>').appendTo(this.element).addClass("ui-slider-handle")}if(c.values&&c.values.length){while(a(".ui-slider-handle",this.element).length<c.values.length){a('<a href="#"></a>').appendTo(this.element).addClass("ui-slider-handle")}}this.handles=a(".ui-slider-handle",this.element).addClass("ui-state-default ui-corner-all");this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(d){d.preventDefault()}).hover(function(){a(this).addClass("ui-state-hover")},function(){a(this).removeClass("ui-state-hover")}).focus(function(){a(".ui-slider .ui-state-focus").removeClass("ui-state-focus");a(this).addClass("ui-state-focus")}).blur(function(){a(this).removeClass("ui-state-focus")});this.handles.each(function(d){a(this).data("index.ui-slider-handle",d)});this.handles.keydown(function(i){var f=true;var e=a(this).data("index.ui-slider-handle");if(b.options.disabled){return}switch(i.keyCode){case a.ui.keyCode.HOME:case a.ui.keyCode.END:case a.ui.keyCode.UP:case a.ui.keyCode.RIGHT:case a.ui.keyCode.DOWN:case a.ui.keyCode.LEFT:f=false;if(!b._keySliding){b._keySliding=true;a(this).addClass("ui-state-active");b._start(i,e)}break}var g,d,h=b._step();if(b.options.values&&b.options.values.length){g=d=b.values(e)}else{g=d=b.value()}switch(i.keyCode){case a.ui.keyCode.HOME:d=b._valueMin();break;case a.ui.keyCode.END:d=b._valueMax();break;case a.ui.keyCode.UP:case a.ui.keyCode.RIGHT:if(g==b._valueMax()){return}d=g+h;break;case a.ui.keyCode.DOWN:case a.ui.keyCode.LEFT:if(g==b._valueMin()){return}d=g-h;break}b._slide(i,e,d);return f}).keyup(function(e){var d=a(this).data("index.ui-slider-handle");if(b._keySliding){b._stop(e,d);b._change(e,d);b._keySliding=false;a(this).removeClass("ui-state-active")}});this._refreshValue()},destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider");this._mouseDestroy()},_mouseCapture:function(d){var e=this.options;if(e.disabled){return false}this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();var h={x:d.pageX,y:d.pageY};var j=this._normValueFromMouse(h);var c=this._valueMax()-this._valueMin()+1,f;var k=this,i;this.handles.each(function(l){var m=Math.abs(j-k.values(l));if(c>m){c=m;f=a(this);i=l}});if(e.range==true&&this.values(1)==e.min){f=a(this.handles[++i])}this._start(d,i);k._handleIndex=i;f.addClass("ui-state-active").focus();var g=f.offset();var b=!a(d.target).parents().andSelf().is(".ui-slider-handle");this._clickOffset=b?{left:0,top:0}:{left:d.pageX-g.left-(f.width()/2),top:d.pageY-g.top-(f.height()/2)-(parseInt(f.css("borderTopWidth"),10)||0)-(parseInt(f.css("borderBottomWidth"),10)||0)+(parseInt(f.css("marginTop"),10)||0)};j=this._normValueFromMouse(h);this._slide(d,i,j);return true},_mouseStart:function(b){return true},_mouseDrag:function(d){var b={x:d.pageX,y:d.pageY};var c=this._normValueFromMouse(b);this._slide(d,this._handleIndex,c);return false},_mouseStop:function(b){this.handles.removeClass("ui-state-active");this._stop(b,this._handleIndex);this._change(b,this._handleIndex);this._handleIndex=null;this._clickOffset=null;return false},_detectOrientation:function(){this.orientation=this.options.orientation=="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(d){var c,h;if("horizontal"==this.orientation){c=this.elementSize.width;h=d.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{c=this.elementSize.height;h=d.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}var f=(h/c);if(f>1){f=1}if(f<0){f=0}if("vertical"==this.orientation){f=1-f}var e=this._valueMax()-this._valueMin(),i=f*e,b=i%this.options.step,g=this._valueMin()+i-b;if(b>(this.options.step/2)){g+=this.options.step}return parseFloat(g.toFixed(5))},_start:function(d,c){var b={handle:this.handles[c],value:this.value()};if(this.options.values&&this.options.values.length){b.value=this.values(c);b.values=this.values()}this._trigger("start",d,b)},_slide:function(f,e,d){var g=this.handles[e];if(this.options.values&&this.options.values.length){var b=this.values(e?0:1);if((e==0&&d>=b)||(e==1&&d<=b)){d=b}if(d!=this.values(e)){var c=this.values();c[e]=d;var h=this._trigger("slide",f,{handle:this.handles[e],value:d,values:c});var b=this.values(e?0:1);if(h!==false){this.values(e,d,(f.type=="mousedown"&&this.options.animate),true)}}}else{if(d!=this.value()){var h=this._trigger("slide",f,{handle:this.handles[e],value:d});if(h!==false){this._setData("value",d,(f.type=="mousedown"&&this.options.animate))}}}},_stop:function(d,c){var b={handle:this.handles[c],value:this.value()};if(this.options.values&&this.options.values.length){b.value=this.values(c);b.values=this.values()}this._trigger("stop",d,b)},_change:function(d,c){var b={handle:this.handles[c],value:this.value()};if(this.options.values&&this.options.values.length){b.value=this.values(c);b.values=this.values()}this._trigger("change",d,b)},value:function(b){if(arguments.length){this._setData("value",b);this._change(null,0)}return this._value()},values:function(b,e,c,d){if(arguments.length>1){this.options.values[b]=e;this._refreshValue(c);if(!d){this._change(null,b)}}if(arguments.length){if(this.options.values&&this.options.values.length){return this._values(b)}else{return this.value()}}else{return this._values()}},_setData:function(b,d,c){a.widget.prototype._setData.apply(this,arguments);switch(b){case"orientation":this._detectOrientation();this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation);this._refreshValue(c);break;case"value":this._refreshValue(c);break}},_step:function(){var b=this.options.step;return b},_value:function(){var b=this.options.value;if(b<this._valueMin()){b=this._valueMin()}if(b>this._valueMax()){b=this._valueMax()}return b},_values:function(b){if(arguments.length){var c=this.options.values[b];if(c<this._valueMin()){c=this._valueMin()}if(c>this._valueMax()){c=this._valueMax()}return c}else{return this.options.values}},_valueMin:function(){var b=this.options.min;return b},_valueMax:function(){var b=this.options.max;return b},_refreshValue:function(c){var f=this.options.range,d=this.options,l=this;if(this.options.values&&this.options.values.length){var i,h;this.handles.each(function(p,n){var o=(l.values(p)-l._valueMin())/(l._valueMax()-l._valueMin())*100;var m={};m[l.orientation=="horizontal"?"left":"bottom"]=o+"%";a(this).stop(1,1)[c?"animate":"css"](m,d.animate);if(l.options.range===true){if(l.orientation=="horizontal"){(p==0)&&l.range.stop(1,1)[c?"animate":"css"]({left:o+"%"},d.animate);(p==1)&&l.range[c?"animate":"css"]({width:(o-lastValPercent)+"%"},{queue:false,duration:d.animate})}else{(p==0)&&l.range.stop(1,1)[c?"animate":"css"]({bottom:(o)+"%"},d.animate);(p==1)&&l.range[c?"animate":"css"]({height:(o-lastValPercent)+"%"},{queue:false,duration:d.animate})}}lastValPercent=o})}else{var j=this.value(),g=this._valueMin(),k=this._valueMax(),e=k!=g?(j-g)/(k-g)*100:0;var b={};b[l.orientation=="horizontal"?"left":"bottom"]=e+"%";this.handle.stop(1,1)[c?"animate":"css"](b,d.animate);(f=="min")&&(this.orientation=="horizontal")&&this.range.stop(1,1)[c?"animate":"css"]({width:e+"%"},d.animate);(f=="max")&&(this.orientation=="horizontal")&&this.range[c?"animate":"css"]({width:(100-e)+"%"},{queue:false,duration:d.animate});(f=="min")&&(this.orientation=="vertical")&&this.range.stop(1,1)[c?"animate":"css"]({height:e+"%"},d.animate);(f=="max")&&(this.orientation=="vertical")&&this.range[c?"animate":"css"]({height:(100-e)+"%"},{queue:false,duration:d.animate})}}}));a.extend(a.ui.slider,{getter:"value values",version:"1.7.1",eventPrefix:"slide",defaults:{animate:false,delay:0,distance:0,max:100,min:0,orientation:"horizontal",range:false,step:1,value:0,values:null}})})(jQuery);;
\ No newline at end of file
--- a/web/facet.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/facet.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,13 +2,15 @@
 a search
 
 :organization: Logilab
-:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
 from itertools import chain
 from copy import deepcopy
+from datetime import date
 
 from logilab.mtconverter import html_escape
 
@@ -19,6 +21,7 @@
 from rql import parse, nodes
 
 from cubicweb import Unauthorized, typed_eid
+from cubicweb.utils import datetime2ticks, make_uid, ustrftime
 from cubicweb.selectors import match_context_prop, partial_relation_possible
 from cubicweb.appobject import AppRsetObject
 from cubicweb.web.htmlwidgets import HTMLWidget
@@ -474,7 +477,6 @@
                                             self.attrtype, self.comparator)
 
 
-
 class FilterRQLBuilder(object):
     """called by javascript to get a rql string from filter form"""
 
@@ -495,6 +497,83 @@
         return select.as_string(), toupdate
 
 
+class RangeFacet(AttributeFacet):
+    attrtype = 'Float' # only numerical types are supported
+
+    @property
+    def wdgclass(self):
+        return FacetRangeWidget
+
+    def get_widget(self):
+        """return the widget instance to use to display this facet
+        """
+        values = set(value for _, value in self.vocabulary() if value is not None)
+        return self.wdgclass(self, min(values), max(values))
+
+    def infvalue(self):
+        return self.req.form.get('%s_inf' % self.id)
+
+    def supvalue(self):
+        return self.req.form.get('%s_sup' % self.id)
+
+    def formatvalue(self, value):
+        """format `value` before in order to insert it in the RQL query"""
+        return unicode(value)
+
+    def add_rql_restrictions(self):
+        infvalue = self.infvalue()
+        if infvalue is None: # nothing sent
+            return
+        supvalue = self.supvalue()
+        self.rqlst.add_constant_restriction(self.filtered_variable,
+                                            self.rtype,
+                                            self.formatvalue(infvalue),
+                                            self.attrtype, '>=')
+        self.rqlst.add_constant_restriction(self.filtered_variable,
+                                            self.rtype,
+                                            self.formatvalue(supvalue),
+                                            self.attrtype, '<=')
+
+class DateRangeFacet(RangeFacet):
+    attrtype = 'Date' # only date types are supported
+
+    @property
+    def wdgclass(self):
+        return DateFacetRangeWidget
+
+    def formatvalue(self, value):
+        """format `value` before in order to insert it in the RQL query"""
+        return '"%s"' % date.fromtimestamp(float(value) / 1000).strftime('%Y/%m/%d')
+
+
+class HasRelationFacet(AbstractFacet):
+    rtype = None # override me in subclass
+    role = 'subject' # role of filtered entity in the relation
+
+    @property
+    def title(self):
+        return display_name(self.req, self.rtype, self.role)
+
+    def support_and(self):
+        return False
+
+    def get_widget(self):
+        return CheckBoxFacetWidget(self.req, self,
+                                   '%s:%s' % (self.rtype, self),
+                                   self.req.form.get(self.id))
+
+    def add_rql_restrictions(self):
+        """add restriction for this facet into the rql syntax tree"""
+        self.rqlst.set_distinct(True) # XXX
+        value = self.req.form.get(self.id)
+        if not value: # no value sent for this facet
+            return
+        var = self.rqlst.make_variable()
+        if self.role == 'subject':
+            self.rqlst.add_relation(self.filtered_variable, self.rtype, var)
+        else:
+            self.rqlst.add_relation(var, self.rtype, self.filtered_variable)
+
 ## html widets ################################################################
 
 class FacetVocabularyWidget(HTMLWidget):
@@ -526,7 +605,7 @@
             cssclass += ' overflowed'
         self.w(u'<div class="facetBody%s">\n' % cssclass)
         for item in self.items:
-            item.render(self.w)
+            item.render(w=self.w)
         self.w(u'</div>\n')
         self.w(u'</div>\n')
 
@@ -546,6 +625,73 @@
         self.w(u'</div>\n')
 
 
+class FacetRangeWidget(HTMLWidget):
+    formatter = 'function (value) {return value;}'
+    onload = u'''
+    var _formatter = %(formatter)s;
+    jQuery("#%(sliderid)s").slider({
+        range: true,
+        min: %(minvalue)s,
+        max: %(maxvalue)s,
+        values: [%(minvalue)s, %(maxvalue)s],
+        stop: function(event, ui) { // submit when the user stops sliding
+           var form = $('#%(sliderid)s').closest('form');
+           buildRQL.apply(null, evalJSON(form.attr('cubicweb:facetargs')));
+        },
+        slide: function(event, ui) {
+            jQuery('#%(sliderid)s_inf').html(_formatter(ui.values[0]));
+            jQuery('#%(sliderid)s_sup').html(_formatter(ui.values[1]));
+            jQuery('input[name=%(facetid)s_inf]').val(ui.values[0]);
+            jQuery('input[name=%(facetid)s_sup]').val(ui.values[1]);
+        }
+   });
+   // use JS formatter to format value on page load
+   jQuery('#%(sliderid)s_inf').html(_formatter(jQuery('input[name=%(facetid)s_inf]').val()));
+   jQuery('#%(sliderid)s_sup').html(_formatter(jQuery('input[name=%(facetid)s_sup]').val()));
+'''
+    #'# make emacs happier
+    def __init__(self, facet, minvalue, maxvalue):
+        self.facet = facet
+        self.minvalue = minvalue
+        self.maxvalue = maxvalue
+
+    def _render(self):
+        facet = self.facet
+        facet.req.add_js('ui.slider.js')
+        facet.req.add_css('ui.all.css')
+        sliderid = make_uid('the slider')
+        facetid = html_escape(self.facet.id)
+        facet.req.html_headers.add_onload(self.onload % {
+            'sliderid': sliderid,
+            'facetid': facetid,
+            'minvalue': self.minvalue,
+            'maxvalue': self.maxvalue,
+            'formatter': self.formatter,
+            })
+        title = html_escape(self.facet.title)
+        self.w(u'<div id="%s" class="facet">\n' % facetid)
+        self.w(u'<div class="facetTitle" cubicweb:facetName="%s">%s</div>\n' %
+               (facetid, title))
+        self.w(u'<span id="%s_inf"></span> - <span id="%s_sup"></span>'
+               % (sliderid, sliderid))
+        self.w(u'<input type="hidden" name="%s_inf" value="%s" />'
+               % (facetid, self.minvalue))
+        self.w(u'<input type="hidden" name="%s_sup" value="%s" />'
+               % (facetid, self.maxvalue))
+        self.w(u'<div id="%s"></div>' % sliderid)
+        self.w(u'</div>\n')
+
+
+class DateFacetRangeWidget(FacetRangeWidget):
+    formatter = 'function (value) {return (new Date(parseFloat(value))).strftime(DATE_FMT);}'
+    def __init__(self, facet, minvalue, maxvalue):
+        super(DateFacetRangeWidget, self).__init__(facet,
+                                                   datetime2ticks(minvalue),
+                                                   datetime2ticks(maxvalue))
+        fmt = facet.req.property_value('ui.date-format')
+        facet.req.html_headers.define_var('DATE_FMT', fmt)
+
+
 class FacetItem(HTMLWidget):
 
     selected_img = "black-check.png"
@@ -572,6 +718,36 @@
         self.w(u'<a href="javascript: {}">%s</a>' % html_escape(self.label))
         self.w(u'</div>')
 
+class CheckBoxFacetWidget(HTMLWidget):
+    selected_img = "black-check.png"
+    unselected_img = "black-uncheck.png"
+
+    def __init__(self, req, facet, value, selected):
+        self.req = req
+        self.facet = facet
+        self.value = value
+        self.selected = selected
+
+    def _render(self):
+        title = html_escape(self.facet.title)
+        facetid = html_escape(self.facet.id)
+        self.w(u'<div id="%s" class="facet">\n' % facetid)
+        if self.selected:
+            cssclass = ' facetValueSelected'
+            imgsrc = self.req.datadir_url + self.selected_img
+            imgalt = self.req._('selected')
+        else:
+            cssclass = ''
+            imgsrc = self.req.datadir_url + self.unselected_img
+            imgalt = self.req._('not selected')
+        self.w(u'<div class="facetValue facetCheckBox%s" cubicweb:value="%s">\n'
+               % (cssclass, html_escape(unicode(self.value))))
+        self.w(u'<div class="facetCheckBoxWidget">')
+        self.w(u'<img src="%s" alt="%s" cubicweb:unselimg="true" />&nbsp;' % (imgsrc, imgalt))
+        self.w(u'<label class="facetTitle" cubicweb:facetName="%s"><a href="javascript: {}">%s</a></label>' % (facetid,title))
+        self.w(u'</div>\n')
+        self.w(u'</div>\n')
+        self.w(u'</div>\n')
 
 class FacetSeparator(HTMLWidget):
     def __init__(self, label=None):
@@ -579,4 +755,3 @@
 
     def _render(self):
         pass
-
--- a/web/form.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/form.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,33 +1,23 @@
 """abstract form classes for CubicWeb web client
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
-from warnings import warn
-
-from logilab.common.compat import any
-from logilab.common.decorators import iclassmethod
-
 from cubicweb.appobject import AppRsetObject
-from cubicweb.selectors import yes, non_final_entity, match_kwargs, one_line_rset
 from cubicweb.view import NOINDEX, NOFOLLOW
 from cubicweb.common import tags
-from cubicweb.web import INTERNAL_FIELD_VALUE, eid_param, stdmsgs
-from cubicweb.web.httpcache import NoHTTPCacheManager
-from cubicweb.web.controller import NAV_FORM_PARAMETERS
-from cubicweb.web.formfields import (Field, StringField, RelationField,
-                                     HiddenInitialValueField)
-from cubicweb.web import formrenderers
-from cubicweb.web import formwidgets as fwdgs
+from cubicweb.web import stdmsgs, httpcache, formfields
+
 
 class FormViewMixIn(object):
     """abstract form view mix-in"""
     category = 'form'
     controller = 'edit'
-    http_cache_manager = NoHTTPCacheManager
+    http_cache_manager = httpcache.NoHTTPCacheManager
     add_to_breadcrumbs = False
 
     def html_headers(self):
@@ -90,7 +80,7 @@
     domid = 'entityForm'
     category = 'form'
     controller = 'edit'
-    http_cache_manager = NoHTTPCacheManager
+    http_cache_manager = httpcache.NoHTTPCacheManager
     add_to_breadcrumbs = False
 
     def html_headers(self):
@@ -198,7 +188,7 @@
             if hasattr(base, '_fields_'):
                 allfields += base._fields_
         clsfields = (item for item in classdict.items()
-                     if isinstance(item[1], Field))
+                     if isinstance(item[1], formfields.Field))
         for fieldname, field in sorted(clsfields, key=lambda x: x[1].creation_rank):
             if not field.name:
                 field.set_name(fieldname)
@@ -212,462 +202,6 @@
     found
     """
 
-class FieldsForm(FormMixIn, AppRsetObject):
+class Form(FormMixIn, AppRsetObject):
     __metaclass__ = metafieldsform
     __registry__ = 'forms'
-    __select__ = yes()
-
-    renderer_cls = formrenderers.FormRenderer
-    is_subform = False
-
-    # attributes overrideable through __init__
-    internal_fields = ('__errorurl',) + NAV_FORM_PARAMETERS
-    needs_js = ('cubicweb.ajax.js', 'cubicweb.edition.js',)
-    needs_css = ('cubicweb.form.css',)
-    domid = 'form'
-    title = None
-    action = None
-    onsubmit = "return freezeFormButtons('%(domid)s');"
-    cssclass = None
-    cssstyle = None
-    cwtarget = None
-    redirect_path = None
-    set_error_url = True
-    copy_nav_params = False
-    form_buttons = None # form buttons (button widgets instances)
-
-    def __init__(self, req, rset=None, row=None, col=None, submitmsg=None,
-                 **kwargs):
-        super(FieldsForm, self).__init__(req, rset, row=row, col=col)
-        self.fields = list(self.__class__._fields_)
-        for key, val in kwargs.items():
-            if key in NAV_FORM_PARAMETERS:
-                self.form_add_hidden(key, val)
-            else:
-                assert hasattr(self.__class__, key) and not key[0] == '_', key
-                setattr(self, key, val)
-        if self.set_error_url:
-            self.form_add_hidden('__errorurl', self.session_key())
-        if self.copy_nav_params:
-            for param in NAV_FORM_PARAMETERS:
-                if not param in kwargs:
-                    value = req.form.get(param)
-                    if value:
-                        self.form_add_hidden(param, value)
-        if submitmsg is not None:
-            self.form_add_hidden('__message', submitmsg)
-        self.context = None
-        if 'domid' in kwargs:# session key changed
-            self.restore_previous_post(self.session_key())
-
-    @iclassmethod
-    def field_by_name(cls_or_self, name, role='subject'):
-        """return field with the given name and role"""
-        if isinstance(cls_or_self, type):
-            fields = cls_or_self._fields_
-        else:
-            fields = cls_or_self.fields
-        for field in fields:
-            if field.name == name and field.role == role:
-                return field
-        raise FieldNotFound(name)
-
-    @iclassmethod
-    def remove_field(cls_or_self, field):
-        """remove a field from form class or instance"""
-        if isinstance(cls_or_self, type):
-            fields = cls_or_self._fields_
-        else:
-            fields = cls_or_self.fields
-        fields.remove(field)
-
-    @iclassmethod
-    def append_field(cls_or_self, field):
-        """append a field to form class or instance"""
-        if isinstance(cls_or_self, type):
-            fields = cls_or_self._fields_
-        else:
-            fields = cls_or_self.fields
-        fields.append(field)
-
-    @property
-    def form_needs_multipart(self):
-        """true if the form needs enctype=multipart/form-data"""
-        return any(field.needs_multipart for field in self.fields)
-
-    def form_add_hidden(self, name, value=None, **kwargs):
-        """add an hidden field to the form"""
-        field = StringField(name=name, widget=fwdgs.HiddenInput, initial=value,
-                            **kwargs)
-        if 'id' in kwargs:
-            # by default, hidden input don't set id attribute. If one is
-            # explicitly specified, ensure it will be set
-            field.widget.setdomid = True
-        self.append_field(field)
-        return field
-
-    def add_media(self):
-        """adds media (CSS & JS) required by this widget"""
-        if self.needs_js:
-            self.req.add_js(self.needs_js)
-        if self.needs_css:
-            self.req.add_css(self.needs_css)
-
-    def form_render(self, **values):
-        """render this form, using the renderer given in args or the default
-        FormRenderer()
-        """
-        renderer = values.pop('renderer', self.renderer_cls())
-        return renderer.render(self, values)
-
-    def form_build_context(self, rendervalues=None):
-        """build form context values (the .context attribute which is a
-        dictionary with field instance as key associated to a dictionary
-        containing field 'name' (qualified), 'id', 'value' (for display, always
-        a string).
-
-        rendervalues is an optional dictionary containing extra kwargs given to
-        form_render()
-        """
-        self.context = context = {}
-        # ensure rendervalues is a dict
-        if rendervalues is None:
-            rendervalues = {}
-        for field in self.fields:
-            for field in field.actual_fields(self):
-                field.form_init(self)
-                value = self.form_field_display_value(field, rendervalues)
-                context[field] = {'value': value,
-                                  'name': self.form_field_name(field),
-                                  'id': self.form_field_id(field),
-                                  }
-
-    def form_field_display_value(self, field, rendervalues, load_bytes=False):
-        """return field's *string* value to use for display
-
-        looks in
-        1. previously submitted form values if any (eg on validation error)
-        2. req.form
-        3. extra kw args given to render_form
-        4. field's typed value
-
-        values found in 1. and 2. are expected te be already some 'display'
-        value while those found in 3. and 4. are expected to be correctly typed.
-        """
-        value = self._req_display_value(field)
-        if value is None:
-            if field.name in rendervalues:
-                value = rendervalues[field.name]
-            else:
-                value = self.form_field_value(field, load_bytes)
-                if callable(value):
-                    value = value(self)
-            if value != INTERNAL_FIELD_VALUE:
-                value = field.format_value(self.req, value)
-        return value
-
-    def _req_display_value(self, field):
-        qname = self.form_field_name(field)
-        if qname in self.form_previous_values:
-            return self.form_previous_values[qname]
-        if qname in self.req.form:
-            return self.req.form[qname]
-        return None
-
-    def form_field_value(self, field, load_bytes=False):
-        """return field's *typed* value"""
-        value = field.initial
-        if callable(value):
-            value = value(self)
-        return value
-
-    def form_field_error(self, field):
-        """return validation error for widget's field, if any"""
-        if self._field_has_error(field):
-            self.form_displayed_errors.add(field.name)
-            return u'<span class="error">%s</span>' % self.form_valerror.errors[field.name]
-        return u''
-
-    def form_field_format(self, field):
-        """return MIME type used for the given (text or bytes) field"""
-        return self.req.property_value('ui.default-text-format')
-
-    def form_field_encoding(self, field):
-        """return encoding used for the given (text) field"""
-        return self.req.encoding
-
-    def form_field_name(self, field):
-        """return qualified name for the given field"""
-        return field.name
-
-    def form_field_id(self, field):
-        """return dom id for the given field"""
-        return field.id
-
-    def form_field_vocabulary(self, field, limit=None):
-        """return vocabulary for the given field. Should be overriden in
-        specific forms using fields which requires some vocabulary
-        """
-        raise NotImplementedError
-
-    def _field_has_error(self, field):
-        """return true if the field has some error in given validation exception
-        """
-        return self.form_valerror and field.name in self.form_valerror.errors
-
-
-class EntityFieldsForm(FieldsForm):
-    __select__ = (match_kwargs('entity') | (one_line_rset & non_final_entity()))
-
-    internal_fields = FieldsForm.internal_fields + ('__type', 'eid', '__maineid')
-    domid = 'entityForm'
-
-    def __init__(self, *args, **kwargs):
-        self.edited_entity = kwargs.pop('entity', None)
-        msg = kwargs.pop('submitmsg', None)
-        super(EntityFieldsForm, self).__init__(*args, **kwargs)
-        if self.edited_entity is None:
-            self.edited_entity = self.complete_entity(self.row or 0, self.col or 0)
-        self.form_add_hidden('__type', eidparam=True)
-        self.form_add_hidden('eid')
-        if msg is not None:
-            # If we need to directly attach the new object to another one
-            for linkto in self.req.list_form_param('__linkto'):
-                self.form_add_hidden('__linkto', linkto)
-                msg = '%s %s' % (msg, self.req._('and linked'))
-            self.form_add_hidden('__message', msg)
-        # in case of direct instanciation
-        self.schema = self.edited_entity.schema
-        self.vreg = self.edited_entity.vreg
-
-    def _field_has_error(self, field):
-        """return true if the field has some error in given validation exception
-        """
-        return super(EntityFieldsForm, self)._field_has_error(field) \
-               and self.form_valerror.eid == self.edited_entity.eid
-
-    def _relation_vocabulary(self, rtype, targettype, role,
-                            limit=None, done=None):
-        """return unrelated entities for a given relation and target entity type
-        for use in vocabulary
-        """
-        if done is None:
-            done = set()
-        rset = self.edited_entity.unrelated(rtype, targettype, role, limit)
-        res = []
-        for entity in rset.entities():
-            if entity.eid in done:
-                continue
-            done.add(entity.eid)
-            res.append((entity.view('combobox'), entity.eid))
-        return res
-
-    def _req_display_value(self, field):
-        value = super(EntityFieldsForm, self)._req_display_value(field)
-        if value is None:
-            value = self.edited_entity.linked_to(field.name, field.role) or None
-        return value
-
-    def _form_field_default_value(self, field, load_bytes):
-        defaultattr = 'default_%s' % field.name
-        if hasattr(self.edited_entity, defaultattr):
-            # XXX bw compat, default_<field name> on the entity
-            warn('found %s on %s, should be set on a specific form'
-                 % (defaultattr, self.edited_entity.id), DeprecationWarning)
-            value = getattr(self.edited_entity, defaultattr)
-            if callable(value):
-                value = value()
-        else:
-            value = super(EntityFieldsForm, self).form_field_value(field,
-                                                                   load_bytes)
-        return value
-
-    def form_build_context(self, values=None):
-        """overriden to add edit[s|o] hidden fields and to ensure schema fields
-        have eidparam set to True
-
-        edit[s|o] hidden fields are used to indicate the value for the
-        associated field before the (potential) modification made when
-        submitting the form.
-        """
-        eschema = self.edited_entity.e_schema
-        for field in self.fields[:]:
-            for field in field.actual_fields(self):
-                fieldname = field.name
-                if fieldname != 'eid' and (
-                    (eschema.has_subject_relation(fieldname) or
-                     eschema.has_object_relation(fieldname))):
-                    field.eidparam = True
-                    self.fields.append(HiddenInitialValueField(field))
-        return super(EntityFieldsForm, self).form_build_context(values)
-
-    def form_field_value(self, field, load_bytes=False):
-        """return field's *typed* value
-
-        overriden to deal with
-        * special eid / __type / edits- / edito- fields
-        * lookup for values on edited entities
-        """
-        attr = field.name
-        entity = self.edited_entity
-        if attr == 'eid':
-            return entity.eid
-        if not field.eidparam:
-            return super(EntityFieldsForm, self).form_field_value(field, load_bytes)
-        if attr.startswith('edits-') or attr.startswith('edito-'):
-            # edit[s|o]- fieds must have the actual value stored on the entity
-            assert hasattr(field, 'visible_field')
-            vfield = field.visible_field
-            assert vfield.eidparam
-            if entity.has_eid():
-                return self.form_field_value(vfield)
-            return INTERNAL_FIELD_VALUE
-        if attr == '__type':
-            return entity.id
-        if self.schema.rschema(attr).is_final():
-            attrtype = entity.e_schema.destination(attr)
-            if attrtype == 'Password':
-                return entity.has_eid() and INTERNAL_FIELD_VALUE or ''
-            if attrtype == 'Bytes':
-                if entity.has_eid():
-                    if load_bytes:
-                        return getattr(entity, attr)
-                    # XXX value should reflect if some file is already attached
-                    return True
-                return False
-            if entity.has_eid() or attr in entity:
-                value = getattr(entity, attr)
-            else:
-                value = self._form_field_default_value(field, load_bytes)
-            return value
-        # non final relation field
-        if entity.has_eid() or entity.relation_cached(attr, field.role):
-            value = [r[0] for r in entity.related(attr, field.role)]
-        else:
-            value = self._form_field_default_value(field, load_bytes)
-        return value
-
-    def form_field_format(self, field):
-        """return MIME type used for the given (text or bytes) field"""
-        entity = self.edited_entity
-        if field.eidparam and entity.e_schema.has_metadata(field.name, 'format') and (
-            entity.has_eid() or '%s_format' % field.name in entity):
-            return self.edited_entity.attr_metadata(field.name, 'format')
-        return self.req.property_value('ui.default-text-format')
-
-    def form_field_encoding(self, field):
-        """return encoding used for the given (text) field"""
-        entity = self.edited_entity
-        if field.eidparam and entity.e_schema.has_metadata(field.name, 'encoding') and (
-            entity.has_eid() or '%s_encoding' % field.name in entity):
-            return self.edited_entity.attr_metadata(field.name, 'encoding')
-        return super(EntityFieldsForm, self).form_field_encoding(field)
-
-    def form_field_name(self, field):
-        """return qualified name for the given field"""
-        if field.eidparam:
-            return eid_param(field.name, self.edited_entity.eid)
-        return field.name
-
-    def form_field_id(self, field):
-        """return dom id for the given field"""
-        if field.eidparam:
-            return eid_param(field.id, self.edited_entity.eid)
-        return field.id
-
-    def form_field_vocabulary(self, field, limit=None):
-        """return vocabulary for the given field"""
-        role, rtype = field.role, field.name
-        method = '%s_%s_vocabulary' % (role, rtype)
-        try:
-            vocabfunc = getattr(self, method)
-        except AttributeError:
-            try:
-                # XXX bw compat, <role>_<rtype>_vocabulary on the entity
-                vocabfunc = getattr(self.edited_entity, method)
-            except AttributeError:
-                vocabfunc = getattr(self, '%s_relation_vocabulary' % role)
-            else:
-                warn('found %s on %s, should be set on a specific form'
-                     % (method, self.edited_entity.id), DeprecationWarning)
-        # NOTE: it is the responsibility of `vocabfunc` to sort the result
-        #       (direclty through RQL or via a python sort). This is also
-        #       important because `vocabfunc` might return a list with
-        #       couples (label, None) which act as separators. In these
-        #       cases, it doesn't make sense to sort results afterwards.
-        return vocabfunc(rtype, limit)
-
-    def subject_relation_vocabulary(self, rtype, limit=None):
-        """defaut vocabulary method for the given relation, looking for
-        relation's object entities (i.e. self is the subject)
-        """
-        entity = self.edited_entity
-        if isinstance(rtype, basestring):
-            rtype = entity.schema.rschema(rtype)
-        done = None
-        assert not rtype.is_final(), rtype
-        if entity.has_eid():
-            done = set(e.eid for e in getattr(entity, str(rtype)))
-        result = []
-        rsetsize = None
-        for objtype in rtype.objects(entity.e_schema):
-            if limit is not None:
-                rsetsize = limit - len(result)
-            result += self._relation_vocabulary(rtype, objtype, 'subject',
-                                                rsetsize, done)
-            if limit is not None and len(result) >= limit:
-                break
-        return result
-
-    def object_relation_vocabulary(self, rtype, limit=None):
-        """defaut vocabulary method for the given relation, looking for
-        relation's subject entities (i.e. self is the object)
-        """
-        entity = self.edited_entity
-        if isinstance(rtype, basestring):
-            rtype = entity.schema.rschema(rtype)
-        done = None
-        if entity.has_eid():
-            done = set(e.eid for e in getattr(entity, 'reverse_%s' % rtype))
-        result = []
-        rsetsize = None
-        for subjtype in rtype.subjects(entity.e_schema):
-            if limit is not None:
-                rsetsize = limit - len(result)
-            result += self._relation_vocabulary(rtype, subjtype, 'object',
-                                                rsetsize, done)
-            if limit is not None and len(result) >= limit:
-                break
-        return result
-
-    def subject_in_state_vocabulary(self, rtype, limit=None):
-        """vocabulary method for the in_state relation, looking for relation's
-        object entities (i.e. self is the subject) according to initial_state,
-        state_of and next_state relation
-        """
-        entity = self.edited_entity
-        if not entity.has_eid() or not entity.in_state:
-            # get the initial state
-            rql = 'Any S where S state_of ET, ET name %(etype)s, ET initial_state S'
-            rset = self.req.execute(rql, {'etype': str(entity.e_schema)})
-            if rset:
-                return [(rset.get_entity(0, 0).view('combobox'), rset[0][0])]
-            return []
-        results = []
-        for tr in entity.in_state[0].transitions(entity):
-            state = tr.destination_state[0]
-            results.append((state.view('combobox'), state.eid))
-        return sorted(results)
-
-
-class CompositeForm(FieldsForm):
-    """form composed for sub-forms"""
-
-    def __init__(self, *args, **kwargs):
-        super(CompositeForm, self).__init__(*args, **kwargs)
-        self.forms = []
-
-    def form_add_subform(self, subform):
-        """mark given form as a subform and append it"""
-        subform.is_subform = True
-        self.forms.append(subform)
--- a/web/formfields.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/formfields.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """field classes for form construction
 
 :organization: Logilab
-:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -19,6 +20,7 @@
     HiddenInput, TextInput, FileInput, PasswordInput, TextArea, FCKEditor,
     Radio, Select, DateTimePicker)
 
+
 class Field(object):
     """field class is introduced to control what's displayed in forms. It makes
     the link between something to edit and its display in the form. Actual
@@ -79,16 +81,13 @@
         self.label = label or name
         self.help = help
         self.required = required
-        if widget is not None:
-            self.widget = widget
-        if isinstance(self.widget, type):
-            self.widget = self.widget()
         self.initial = initial
         self.choices = choices
         self.sort = sort
         self.internationalizable = internationalizable
         self.eidparam = eidparam
         self.role = role
+        self.init_widget(widget)
         # ordering number for this field instance
         self.creation_rank = Field.__creation_rank
         Field.__creation_rank += 1
@@ -101,6 +100,14 @@
     def __repr__(self):
         return self.__unicode__().encode('utf-8')
 
+    def init_widget(self, widget):
+        if widget is None and self.choices:
+            widget = Select()
+        if widget is not None:
+            self.widget = widget
+        if isinstance(self.widget, type):
+            self.widget = self.widget()
+
     def set_name(self, name):
         """automatically set .id and .label when name is set"""
         assert name
@@ -176,10 +183,21 @@
         """
         pass
 
+
 class StringField(Field):
+    widget = TextArea
+
     def __init__(self, max_length=None, **kwargs):
+        self.max_length = max_length # must be set before super call
         super(StringField, self).__init__(**kwargs)
-        self.max_length = max_length
+
+    def init_widget(self, widget):
+        if widget is None:
+            if self.choices:
+                widget = Select()
+            elif self.max_length and self.max_length < 257:
+                widget = TextInput()
+        super(StringField, self).init_widget(widget)
         if isinstance(self.widget, TextArea):
             self.init_text_area(self.widget)
 
@@ -213,20 +231,20 @@
         try:
             return req.data[self]
         except KeyError:
+            fkwargs = {}
             if self.use_fckeditor(form):
                 # if fckeditor is used and format field isn't explicitly
                 # deactivated, we want an hidden field for the format
-                widget = HiddenInput()
-                choices = None
+                fkwargs['widget'] = HiddenInput()
+                fkwargs['initial'] = 'text/html'
             else:
                 # else we want a format selector
-                # XXX compute vocabulary
-                widget = Select()
+                fkwargs['widget'] = Select()
                 fcstr = FormatConstraint()
-                choices = [(req._(fmt), fmt) for fmt in fcstr.vocabulary(req=req)]
-                widget.attrs['size'] = 1
-            field = StringField(name=self.name + '_format', widget=widget,
-                                choices=choices)
+                fkwargs['choices'] = fcstr.vocabulary(req=req)
+                fkwargs['internationalizable'] = True
+                fkwargs['initial'] = lambda f: f.form_field_format(self)
+            field = StringField(name=self.name + '_format', **fkwargs)
             req.data[self] = field
             return field
 
@@ -340,6 +358,7 @@
             self.widget.attrs.setdefault('size', 5)
             self.widget.attrs.setdefault('maxlength', 15)
 
+
 class BooleanField(Field):
     widget = Radio
 
@@ -379,6 +398,7 @@
     format_prop = 'ui.datetime-format'
     widget = TextInput
 
+
 class HiddenInitialValueField(Field):
     def __init__(self, visible_field):
         name = 'edit%s-%s' % (visible_field.role[0], visible_field.name)
@@ -468,13 +488,10 @@
             # init StringField parameters according to constraints
             for cstr in constraints:
                 if isinstance(cstr, StaticVocabularyConstraint):
-                    kwargs.setdefault('widget', Select())
                     kwargs.setdefault('choices', cstr.vocabulary)
-                    if card in '?1':
-                        kwargs['widget'].attrs.setdefault('size', 1)
+                    break
+            for cstr in constraints:
                 if isinstance(cstr, SizeConstraint) and cstr.max is not None:
-                    if cstr.max > 257:
-                        kwargs.setdefault('widget', TextArea)
                     kwargs['max_length'] = cstr.max
             return StringField(**kwargs)
         if fieldclass is FileField:
@@ -487,6 +504,7 @@
     kwargs['role'] = role
     return RelationField.fromcardinality(card, **kwargs)
 
+
 FIELDS = {
     'Boolean':  BooleanField,
     'Bytes':    FileField,
--- a/web/formrenderers.py	Wed Jun 03 09:09:33 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,474 +0,0 @@
-"""form renderers, responsible to layout a form to html
-
-:organization: Logilab
-:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-"""
-__docformat__ = "restructuredtext en"
-
-from logilab.common import dictattr
-from logilab.mtconverter import html_escape
-
-from simplejson import dumps
-
-from cubicweb.common import tags
-from cubicweb.web import eid_param
-from cubicweb.web import formwidgets as fwdgs
-from cubicweb.web.widgets import checkbox
-from cubicweb.web.formfields import HiddenInitialValueField
-
-
-class FormRenderer(object):
-    """basic renderer displaying fields in a two columns table label | value
-
-    +--------------+--------------+
-    | field1 label | field1 input |
-    +--------------+--------------+
-    | field1 label | field2 input |
-    +--------------+--------------+
-    +---------+
-    | buttons |
-    +---------+
-    """
-    _options = ('display_fields', 'display_label', 'display_help',
-                'display_progress_div', 'table_class', 'button_bar_class')
-    display_fields = None # None -> all fields
-    display_label = True
-    display_help = True
-    display_progress_div = True
-    table_class = u'attributeForm'
-    button_bar_class = u'formButtonBar'
-
-    def __init__(self, **kwargs):
-        if self._set_options(kwargs):
-            raise ValueError('unconsumed arguments %s' % kwargs)
-
-    def _set_options(self, kwargs):
-        for key in self._options:
-            try:
-                setattr(self, key, kwargs.pop(key))
-            except KeyError:
-                continue
-        return kwargs
-
-    # renderer interface ######################################################
-
-    def render(self, form, values):
-        self._set_options(values)
-        form.add_media()
-        data = []
-        w = data.append
-        w(self.open_form(form, values))
-        if self.display_progress_div:
-            w(u'<div id="progress">%s</div>' % form.req._('validating...'))
-        w(u'<fieldset>')
-        w(tags.input(type=u'hidden', name=u'__form_id',
-                     value=values.get('formvid', form.id)))
-        if form.redirect_path:
-            w(tags.input(type='hidden', name='__redirectpath', value=form.redirect_path))
-        self.render_fields(w, form, values)
-        self.render_buttons(w, form)
-        w(u'</fieldset>')
-        w(u'</form>')
-        errormsg = self.error_message(form)
-        if errormsg:
-            data.insert(0, errormsg)
-        return '\n'.join(data)
-
-    def render_label(self, form, field):
-        label = form.req._(field.label)
-        attrs = {'for': form.context[field]['id']}
-        if field.required:
-            attrs['class'] = 'required'
-        return tags.label(label, **attrs)
-
-    def render_help(self, form, field):
-        help = [ u'<br/>' ]
-        descr = field.help
-        if descr:
-            help.append('<span class="helper">%s</span>' % form.req._(descr))
-        example = field.example_format(form.req)
-        if example:
-            help.append('<span class="helper">(%s: %s)</span>'
-                        % (form.req._('sample format'), example))
-        return u'&nbsp;'.join(help)
-
-    # specific methods (mostly to ease overriding) #############################
-
-    def error_message(self, form):
-        """return formatted error message
-
-        This method should be called once inlined field errors has been consumed
-        """
-        req = form.req
-        errex = form.form_valerror
-        # get extra errors
-        if errex is not None:
-            errormsg = req._('please correct the following errors:')
-            displayed = form.form_displayed_errors
-            errors = sorted((field, err) for field, err in errex.errors.items()
-                            if not field in displayed)
-            if errors:
-                if len(errors) > 1:
-                    templstr = '<li>%s</li>\n'
-                else:
-                    templstr = '&nbsp;%s\n'
-                for field, err in errors:
-                    if field is None:
-                        errormsg += templstr % err
-                    else:
-                        errormsg += templstr % '%s: %s' % (req._(field), err)
-                if len(errors) > 1:
-                    errormsg = '<ul>%s</ul>' % errormsg
-            return u'<div class="errorMessage">%s</div>' % errormsg
-        return u''
-
-    def open_form(self, form, values):
-        if form.form_needs_multipart:
-            enctype = 'multipart/form-data'
-        else:
-            enctype = 'application/x-www-form-urlencoded'
-        if form.action is None:
-            action = form.req.build_url('edit')
-        else:
-            action = form.action
-        tag = ('<form action="%s" method="post" enctype="%s"' % (
-            html_escape(action or '#'), enctype))
-        if form.domid:
-            tag += ' id="%s"' % form.domid
-        if form.onsubmit:
-            tag += ' onsubmit="%s"' % html_escape(form.onsubmit % dictattr(form))
-        if form.cssstyle:
-            tag += ' style="%s"' % html_escape(form.cssstyle)
-        if form.cssclass:
-            tag += ' class="%s"' % html_escape(form.cssclass)
-        if form.cwtarget:
-            tag += ' cubicweb:target="%s"' % html_escape(form.cwtarget)
-        return tag + '>'
-
-    def display_field(self, form, field):
-        if isinstance(field, HiddenInitialValueField):
-            field = field.visible_field
-        return (self.display_fields is None
-                or field.name in form.internal_fields
-                or (field.name, field.role) in self.display_fields
-                or (field.name, field.role) in form.internal_fields)
-
-    def render_fields(self, w, form, values):
-        form.form_build_context(values)
-        fields = self._render_hidden_fields(w, form)
-        if fields:
-            self._render_fields(fields, w, form)
-        self.render_child_forms(w, form, values)
-
-    def render_child_forms(self, w, form, values):
-        # render
-        for childform in getattr(form, 'forms', []):
-            self.render_fields(w, childform, values)
-
-    def _render_hidden_fields(self, w, form):
-        fields = form.fields[:]
-        for field in form.fields:
-            if not self.display_field(form, field):
-                fields.remove(field)
-            elif not field.is_visible():
-                w(field.render(form, self))
-                fields.remove(field)
-        return fields
-
-    def _render_fields(self, fields, w, form):
-        w(u'<table class="%s">' % self.table_class)
-        for field in fields:
-            w(u'<tr>')
-            if self.display_label:
-                w(u'<th class="labelCol">%s</th>' % self.render_label(form, field))
-            error = form.form_field_error(field)
-            if error:
-                w(u'<td class="error">')
-                w(error)
-            else:
-                w(u'<td>')
-            w(field.render(form, self))
-            if self.display_help:
-                w(self.render_help(form, field))
-            w(u'</td></tr>')
-        w(u'</table>')
-
-    def render_buttons(self, w, form):
-        w(u'<table class="%s">\n<tr>\n' % self.button_bar_class)
-        for button in form.form_buttons:
-            w(u'<td>%s</td>\n' % button.render(form))
-        w(u'</tr></table>')
-
-
-class HTableFormRenderer(FormRenderer):
-    """display fields horizontally in a table
-
-    +--------------+--------------+---------+
-    | field1 label | field2 label |         |
-    +--------------+--------------+---------+
-    | field1 input | field2 input | buttons
-    +--------------+--------------+---------+
-    """
-    display_help = False
-    def _render_fields(self, fields, w, form):
-        w(u'<table border="0">')
-        w(u'<tr>')
-        for field in fields:
-            if self.display_label:
-                w(u'<th class="labelCol">%s</th>' % self.render_label(form, field))
-            if self.display_help:
-                w(self.render_help(form, field))
-        # empty slot for buttons
-        w(u'<th class="labelCol">&nbsp;</th>')
-        w(u'</tr>')
-        w(u'<tr>')
-        for field in fields:
-            error = form.form_field_error(field)
-            if error:
-                w(u'<td class="error">')
-                w(error)
-            else:
-                w(u'<td>')
-            w(field.render(form, self))
-            w(u'</td>')
-        w(u'<td>')
-        for button in form.form_buttons:
-            w(button.render(form))
-        w(u'</td>')
-        w(u'</tr>')
-        w(u'</table>')
-
-    def render_buttons(self, w, form):
-        pass
-
-
-class EntityCompositeFormRenderer(FormRenderer):
-    """specific renderer for multiple entities edition form (muledit)"""
-
-    def render_fields(self, w, form, values):
-        if not form.is_subform:
-            w(u'<table class="listing">')
-        super(EntityCompositeFormRenderer, self).render_fields(w, form, values)
-        if not form.is_subform:
-            w(u'</table>')
-
-    def _render_fields(self, fields, w, form):
-        if form.is_subform:
-            entity = form.edited_entity
-            values = form.form_previous_values
-            qeid = eid_param('eid', entity.eid)
-            cbsetstate = "setCheckboxesState2('eid', %s, 'checked')" % html_escape(dumps(entity.eid))
-            w(u'<tr class="%s">' % (entity.row % 2 and u'even' or u'odd'))
-            # XXX turn this into a widget used on the eid field
-            w(u'<td>%s</td>' % checkbox('eid', entity.eid, checked=qeid in values))
-            for field in fields:
-                error = form.form_field_error(field)
-                if error:
-                    w(u'<td class="error">')
-                    w(error)
-                else:
-                    w(u'<td>')
-                if isinstance(field.widget, (fwdgs.Select, fwdgs.CheckBox, fwdgs.Radio)):
-                    field.widget.attrs['onchange'] = cbsetstate
-                elif isinstance(field.widget, fwdgs.Input):
-                    field.widget.attrs['onkeypress'] = cbsetstate
-                w(u'<div>%s</div>' % field.render(form, self))
-                w(u'</td>')
-        else:
-            # main form, display table headers
-            w(u'<tr class="header">')
-            w(u'<th align="left">%s</th>'
-              % tags.input(type='checkbox', title=form.req._('toggle check boxes'),
-                           onclick="setCheckboxesState('eid', this.checked)"))
-            for field in self.forms[0].fields:
-                if self.display_field(form, field) and field.is_visible():
-                    w(u'<th>%s</th>' % form.req._(field.label))
-        w(u'</tr>')
-
-
-
-class EntityFormRenderer(FormRenderer):
-    """specific renderer for entity edition form (edition)"""
-    _options = FormRenderer._options + ('display_relations_form',)
-    display_relations_form = True
-
-    def render(self, form, values):
-        rendered = super(EntityFormRenderer, self).render(form, values)
-        return rendered + u'</div>' # close extra div introducted by open_form
-
-    def open_form(self, form, values):
-        attrs_fs_label = ('<div class="iformTitle"><span>%s</span></div>'
-                          % form.req._('main informations'))
-        attrs_fs_label += '<div class="formBody">'
-        return attrs_fs_label + super(EntityFormRenderer, self).open_form(form, values)
-
-    def render_fields(self, w, form, values):
-        super(EntityFormRenderer, self).render_fields(w, form, values)
-        self.inline_entities_form(w, form)
-        if form.edited_entity.has_eid() and self.display_relations_form:
-            self.relations_form(w, form)
-
-    def _render_fields(self, fields, w, form):
-        if not form.edited_entity.has_eid() or form.edited_entity.has_perm('update'):
-            super(EntityFormRenderer, self)._render_fields(fields, w, form)
-
-    def render_buttons(self, w, form):
-        if len(form.form_buttons) == 3:
-            w("""<table width="100%%">
-  <tbody>
-   <tr><td align="center">
-     %s
-   </td><td style="align: right; width: 50%%;">
-     %s
-     %s
-   </td></tr>
-  </tbody>
- </table>""" % tuple(button.render(form) for button in form.form_buttons))
-        else:
-            super(EntityFormRenderer, self).render_buttons(w, form)
-
-    def relations_form(self, w, form):
-        srels_by_cat = form.srelations_by_category('generic', 'add')
-        if not srels_by_cat:
-            return u''
-        req = form.req
-        _ = req._
-        label = u'%s :' % _('This %s' % form.edited_entity.e_schema).capitalize()
-        eid = form.edited_entity.eid
-        w(u'<fieldset class="subentity">')
-        w(u'<legend class="iformTitle">%s</legend>' % label)
-        w(u'<table id="relatedEntities">')
-        for rschema, target, related in form.relations_table():
-            # already linked entities
-            if related:
-                w(u'<tr><th class="labelCol">%s</th>' % rschema.display_name(req, target))
-                w(u'<td>')
-                w(u'<ul>')
-                for viewparams in related:
-                    w(u'<li class="invisible">%s<div id="span%s" class="%s">%s</div></li>'
-                      % (viewparams[1], viewparams[0], viewparams[2], viewparams[3]))
-                if not form.force_display and form.maxrelitems < len(related):
-                    link = (u'<span class="invisible">'
-                            '[<a href="javascript: window.location.href+=\'&amp;__force_display=1\'">%s</a>]'
-                            '</span>' % form.req._('view all'))
-                    w(u'<li class="invisible">%s</li>' % link)
-                w(u'</ul>')
-                w(u'</td>')
-                w(u'</tr>')
-        pendings = list(form.restore_pending_inserts())
-        if not pendings:
-            w(u'<tr><th>&nbsp;</th><td>&nbsp;</td></tr>')
-        else:
-            for row in pendings:
-                # soon to be linked to entities
-                w(u'<tr id="tr%s">' % row[1])
-                w(u'<th>%s</th>' % row[3])
-                w(u'<td>')
-                w(u'<a class="handle" title="%s" href="%s">[x]</a>' %
-                  (_('cancel this insert'), row[2]))
-                w(u'<a id="a%s" class="editionPending" href="%s">%s</a>'
-                  % (row[1], row[4], html_escape(row[5])))
-                w(u'</td>')
-                w(u'</tr>')
-        w(u'<tr id="relationSelectorRow_%s" class="separator">' % eid)
-        w(u'<th class="labelCol">')
-        w(u'<span>%s</span>' % _('add relation'))
-        w(u'<select id="relationSelector_%s" tabindex="%s" '
-          'onchange="javascript:showMatchingSelect(this.options[this.selectedIndex].value,%s);">'
-          % (eid, req.next_tabindex(), html_escape(dumps(eid))))
-        w(u'<option value="">%s</option>' % _('select a relation'))
-        for i18nrtype, rschema, target in srels_by_cat:
-            # more entities to link to
-            w(u'<option value="%s_%s">%s</option>' % (rschema, target, i18nrtype))
-        w(u'</select>')
-        w(u'</th>')
-        w(u'<td id="unrelatedDivs_%s"></td>' % eid)
-        w(u'</tr>')
-        w(u'</table>')
-        w(u'</fieldset>')
-
-    def inline_entities_form(self, w, form):
-        """create a form to edit entity's inlined relations"""
-        entity = form.edited_entity
-        __ = form.req.__
-        for rschema, targettypes, role in form.inlined_relations():
-            # show inline forms only if there's one possible target type
-            # for rschema
-            if len(targettypes) != 1:
-                self.warning('entity related by the %s relation should have '
-                             'inlined form but there is multiple target types, '
-                             'dunno what to do', rschema)
-                continue
-            targettype = targettypes[0].type
-            if form.should_inline_relation_form(rschema, targettype, role):
-                w(u'<div id="inline%sslot">' % rschema)
-                existant = entity.has_eid() and entity.related(rschema)
-                if existant:
-                    # display inline-edition view for all existing related entities
-                    w(form.view('inline-edition', existant, rtype=rschema, role=role,
-                                ptype=entity.e_schema, peid=entity.eid))
-                if role == 'subject':
-                    card = rschema.rproperty(entity.e_schema, targettype, 'cardinality')[0]
-                else:
-                    card = rschema.rproperty(targettype, entity.e_schema, 'cardinality')[1]
-                # there is no related entity and we need at least one: we need to
-                # display one explicit inline-creation view
-                if form.should_display_inline_creation_form(rschema, existant, card):
-                    w(form.view('inline-creation', None, etype=targettype,
-                                peid=entity.eid, ptype=entity.e_schema,
-                                rtype=rschema, role=role))
-                # we can create more than one related entity, we thus display a link
-                # to add new related entities
-                if form.should_display_add_new_relation_link(rschema, existant, card):
-                    divid = "addNew%s%s%s:%s" % (targettype, rschema, role, entity.eid)
-                    w(u'<div class="inlinedform" id="%s" cubicweb:limit="true">'
-                      % divid)
-                    js = "addInlineCreationForm('%s', '%s', '%s', '%s')" % (
-                        entity.eid, targettype, rschema, role)
-                    if card in '1?':
-                        js = "toggleVisibility('%s'); %s" % (divid, js)
-                    w(u'<a class="addEntity" id="add%s:%slink" href="javascript: %s" >+ %s.</a>'
-                      % (rschema, entity.eid, js, __('add a %s' % targettype)))
-                    w(u'</div>')
-                    w(u'<div class="trame_grise">&nbsp;</div>')
-                w(u'</div>')
-
-
-class EntityInlinedFormRenderer(EntityFormRenderer):
-    """specific renderer for entity inlined edition form
-    (inline-[creation|edition])
-    """
-    def render(self, form, values):
-        form.add_media()
-        data = []
-        w = data.append
-        try:
-            w(u'<div id="div-%(divid)s" onclick="%(divonclick)s">' % values)
-        except KeyError:
-            w(u'<div id="div-%(divid)s">' % values)
-        else:
-            w(u'<div id="notice-%s" class="notice">%s</div>' % (
-                values['divid'], form.req._('click on the box to cancel the deletion')))
-        w(u'<div class="iformBody">')
-        values['removemsg'] = form.req.__('remove this %s' % form.edited_entity.e_schema)
-        w(u'<div class="iformTitle"><span>%(title)s</span> '
-          '#<span class="icounter">1</span> '
-          '[<a href="javascript: %(removejs)s;noop();">%(removemsg)s</a>]</div>'
-          % values)
-        self.render_fields(w, form, values)
-        w(u'</div></div>')
-        return '\n'.join(data)
-
-    def render_fields(self, w, form, values):
-        form.form_build_context(values)
-        w(u'<fieldset id="fs-%(divid)s">' % values)
-        fields = self._render_hidden_fields(w, form)
-        w(u'</fieldset>')
-        w(u'<fieldset class="subentity">')
-        if fields:
-            self._render_fields(fields, w, form)
-        self.render_child_forms(w, form, values)
-        self.inline_entities_form(w, form)
-        w(u'</fieldset>')
-
--- a/web/formwidgets.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/formwidgets.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,12 +1,14 @@
 """widget classes for form construction
 
 :organization: Logilab
-:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
 from datetime import date
+from warnings import warn
 
 from cubicweb.common import tags
 from cubicweb.web import stdmsgs, INTERNAL_FIELD_VALUE
@@ -109,6 +111,11 @@
         return u'\n'.join(inputs)
 
 
+class PasswordSingleInput(Input):
+    """<input type='password'> without a confirmation field"""
+    type = 'password'
+
+
 class FileInput(Input):
     """<input type='file'>"""
     type = 'file'
@@ -170,8 +177,8 @@
 
     def render(self, form, field):
         name, curvalues, attrs = self._render_attrs(form, field)
-        if not 'size' in attrs and self._multiple:
-            attrs['size'] = '5'
+        if not 'size' in attrs:
+            attrs['size'] = self._multiple and '5' or '1'
         options = []
         optgroup_opened = False
         for label, value in field.vocabulary(form):
@@ -200,6 +207,7 @@
     def render(self, form, field):
         name, curvalues, attrs = self._render_attrs(form, field)
         domid = attrs.pop('id', None)
+        sep = attrs.pop('separator', u'<br/>')
         options = []
         for i, (label, value) in enumerate(field.vocabulary(form)):
             iattrs = attrs.copy()
@@ -208,7 +216,7 @@
             if value in curvalues:
                 iattrs['checked'] = u'checked'
             tag = tags.input(name=name, type=self.type, value=value, **iattrs)
-            options.append(tag + label + '<br/>')
+            options.append(tag + label + sep)
         return '\n'.join(options)
 
 
@@ -298,26 +306,45 @@
     wdgtype = 'SuggestField'
     loadtype = 'auto'
 
+    def __init__(self, *args, **kwargs):
+        try:
+            self.autocomplete_initfunc = kwargs.pop('autocomplete_initfunc')
+        except KeyError:
+            warn('use autocomplete_initfunc argument of %s constructor '
+                 'instead of relying on autocomplete_initfuncs dictionary on '
+                 'the entity class' % self.__class__.__name__,
+                 DeprecationWarning)
+            self.autocomplete_initfunc = None
+        super(AutoCompletionWidget, self).__init__(*args, **kwargs)
+
     def _render_attrs(self, form, field):
         name, values, attrs = super(AutoCompletionWidget, self)._render_attrs(form, field)
-        if not values[0]:
-            values = (INTERNAL_FIELD_VALUE,)
         init_ajax_attributes(attrs, self.wdgtype, self.loadtype)
         # XXX entity form specific
-        attrs['cubicweb:dataurl'] = self._get_url(form.edited_entity)
+        attrs['cubicweb:dataurl'] = self._get_url(form.edited_entity, field)
         return name, values, attrs
 
-    def _get_url(self, entity):
-        return entity.req.build_url('json', fname=entity.autocomplete_initfuncs[self.rschema],
-                                pageid=entity.req.pageid, mode='remote')
+    def _get_url(self, entity, field):
+        if self.autocomplete_initfunc is None:
+            # XXX for bw compat
+            fname = entity.autocomplete_initfuncs[field.name]
+        else:
+            fname = self.autocomplete_initfunc
+        return entity.req.build_url('json', fname=fname, mode='remote',
+                                    pageid=entity.req.pageid)
 
 
 class StaticFileAutoCompletionWidget(AutoCompletionWidget):
     """XXX describe me"""
     wdgtype = 'StaticFileSuggestField'
 
-    def _get_url(self, entity):
-        return entity.req.datadir_url + entity.autocomplete_initfuncs[self.rschema]
+    def _get_url(self, entity, field):
+        if self.autocomplete_initfunc is None:
+            # XXX for bw compat
+            fname = entity.autocomplete_initfuncs[field.name]
+        else:
+            fname = self.autocomplete_initfunc
+        return entity.req.datadir_url + fname
 
 
 class RestrictedAutoCompletionWidget(AutoCompletionWidget):
--- a/web/htmlwidgets.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/htmlwidgets.py	Wed Jun 03 09:15:20 2009 +0200
@@ -4,8 +4,9 @@
 serialization time
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 from logilab.mtconverter import html_escape
@@ -57,6 +58,7 @@
     def append(self, item):
         self.items.append(item)
 
+    title_class = 'boxTitle'
     main_div_class = 'boxContent'
     listing_class = 'boxListing'
 
@@ -82,7 +84,7 @@
                 title = '<span>%s</span>' % html_escape(self.title)
             else:
                 title = '<span>%s</span>' % self.title
-            self.w(u'<div class="boxTitle">%s</div>' % title)
+            self.w(u'<div class="%s">%s</div>' % (self.title_class, title))
         if self.items:
             self.box_begin_content()
             for item in self.items:
@@ -93,6 +95,7 @@
 
 class SideBoxWidget(BoxWidget):
     """default CubicWeb's sidebox widget"""
+    title_class = u'sideBoxTitle'
     main_div_class = u'sideBoxBody'
     listing_class = ''
 
--- a/web/httpcache.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/httpcache.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -42,14 +43,14 @@
 
     def etag(self):
         return self.view.id + '/' + ','.join(sorted(self.req.user.groups))
-    
+
     def max_age(self):
         # 0 to actually force revalidation
         return 0
-    
+
     def last_modified(self):
         return self.view.last_modified()
-    
+
     def set_headers(self):
         req = self.req
         try:
@@ -129,4 +130,4 @@
 viewmod.EntityView.http_cache_manager = EntityHTTPCacheManager
 
 viewmod.StartupView.http_cache_manager = MaxAgeHTTPCacheManager
-viewmod.StartupView.cache_max_age = 60*60*2 # stay in http cache for 2 hours by default 
+viewmod.StartupView.cache_max_age = 60*60*2 # stay in http cache for 2 hours by default
--- a/web/request.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/request.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """abstract class for http request
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -19,12 +20,14 @@
 from logilab.common.decorators import cached
 from logilab.common.deprecation import obsolete
 
+from logilab.mtconverter import html_escape
+
 from cubicweb.dbapi import DBAPIRequest
 from cubicweb.common.mail import header
 from cubicweb.common.uilib import remove_html_tags
 from cubicweb.utils import SizeConstrainedList, HTMLHead
-from cubicweb.web import (INTERNAL_FIELD_VALUE, LOGGER, NothingToEdit, RequestError,
-                          StatusResponse)
+from cubicweb.web import (INTERNAL_FIELD_VALUE, LOGGER, NothingToEdit,
+                          RequestError, StatusResponse)
 
 _MARKER = object()
 
@@ -478,6 +481,23 @@
                 cssfile = self.datadir_url + cssfile
             add_css(cssfile, media)
 
+    def build_ajax_replace_url(self, nodeid, rql, vid, replacemode='replace',
+                               **extraparams):
+        """builds an ajax url that will replace `nodeid`s content
+        :param nodeid: the dom id of the node to replace
+        :param rql: rql to execute
+        :param vid: the view to apply on the resultset
+        :param replacemode: defines how the replacement should be done.
+        Possible values are :
+         - 'replace' to replace the node's content with the generated HTML
+         - 'swap' to replace the node itself with the generated HTML
+         - 'append' to append the generated HTML to the node's content
+        """
+        url = self.build_url('view', rql=rql, vid=vid, __notemplate=1,
+                             **extraparams)
+        return "javascript: loadxhtml('%s', '%s', '%s')" % (
+            nodeid, html_escape(url), replacemode)
+
     # urls/path management ####################################################
 
     def url(self, includeparams=True):
--- a/web/test/data/schema/testschema.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/data/schema/testschema.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,7 +1,15 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 class Salesterm(EntityType):
     described_by_test = SubjectRelation('File', cardinality='1*', composite='subject')
     amount = Int(constraints=[IntervalBoundConstraint(0, 100)])
-    
+    reason = String(maxsize=20, vocabulary=[u'canceled', u'sold'])
+
 class tags(RelationDefinition):
     subject = 'Tag'
     object = ('BlogEntry', 'CWUser')
--- a/web/test/data/views.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/data/views.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from cubicweb.web import Redirect
 from cubicweb.web.application import CubicWebPublisher
 
@@ -21,6 +28,6 @@
             pass
         assert req.user.login == login
     return orig_publish(self, path, req)
-    
+
 orig_publish = CubicWebPublisher.main_publish
 CubicWebPublisher.main_publish = auto_login_publish
--- a/web/test/test_views.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/test_views.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""automatic tests"""
+"""automatic tests
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 from cubicweb.devtools.testlib import WebTest, AutomaticWebTest
 from cubicweb.view import AnyRsetView
@@ -22,7 +28,7 @@
 
 class SomeView(AnyRsetView):
     id = 'someview'
-    
+
     def call(self):
         self.req.add_js('spam.js')
         self.req.add_js('spam.js')
@@ -34,7 +40,7 @@
 
     def test_manual_tests(self):
         rset = self.execute('Any P,F,S WHERE P is CWUser, P firstname F, P surname S')
-        self.view('table', rset, template=None, displayfilter=True, displaycols=[0,2])        
+        self.view('table', rset, template=None, displayfilter=True, displaycols=[0,2])
 
     def test_sortable_js_added(self):
         rset = self.execute('CWUser X')
@@ -54,14 +60,13 @@
 
 
 class ExplicitViewsTest(WebTest):
-    
+
     def test_unrelateddivs(self):
         rset = self.execute('Any X WHERE X is CWUser, X login "admin"')
         group = self.add_entity('CWGroup', name=u'R&D')
         req = self.request(relation='in_group_subject')
         self.view('unrelateddivs', rset, req)
-        
-        
+
 
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main
--- a/web/test/unittest_application.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/unittest_application.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,5 +1,11 @@
 # -*- coding: iso-8859-1 -*-
-"""unit tests for cubicweb.web.application"""
+"""unit tests for cubicweb.web.application
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 from logilab.common.testlib import TestCase, unittest_main
 import base64, Cookie
@@ -44,7 +50,7 @@
 class RequestBaseTC(TestCase):
     def setUp(self):
         self.req = FakeRequest()
-        
+
 
     def test_list_arg(self):
         """tests the list_arg() function"""
@@ -73,13 +79,13 @@
         self.assertEquals(req.relative_path(False), 'login')
         self.assertEquals(req.from_controller(), 'login')
 
-        
+
 class UtilsTC(TestCase):
     """test suite for misc application utilities"""
 
     def setUp(self):
         self.ctrl = FakeController()
-    
+
     #def test_which_mapping(self):
     #    """tests which mapping is used (application or core)"""
     #    init_mapping()
@@ -95,7 +101,7 @@
         self.assertEquals(self.ctrl.execute_linkto(), None)
         self.assertEquals(self.ctrl._cursor.executed,
                           [])
-        
+
         self.ctrl.set_form({'__linkto' : 'works_for:12_13_14:object',
                               'eid': 8})
         self.ctrl.execute_linkto()
@@ -110,7 +116,7 @@
         self.assertEquals(self.ctrl._cursor.executed,
                           ['SET X works_for Y WHERE X eid 8, Y eid %s' % i
                            for i in (12, 13, 14)])
-        
+
 
         self.ctrl.new_cursor()
         self.ctrl.req.form = {'__linkto' : 'works_for:12_13_14:object'}
@@ -152,10 +158,10 @@
             return path, params
         else:
             self.fail('expected a Redirect exception')
-        
+
     def expect_redirect_publish(self, req, path='view'):
         return self.expect_redirect(lambda x: self.publish(x, path), req)
-    
+
     def test_cnx_user_groups_sync(self):
         user = self.user()
         self.assertEquals(user.groups, set(('managers',)))
@@ -168,16 +174,16 @@
         # cleanup
         self.execute('DELETE X in_group G WHERE X eid %s, G name "guests"' % user.eid)
         self.commit()
-    
+
     def test_nonregr_publish1(self):
         req = self.request(u'CWEType X WHERE X final FALSE, X meta FALSE')
         self.app.publish('view', req)
-        
+
     def test_nonregr_publish2(self):
         req = self.request(u'Any count(N) WHERE N todo_by U, N is Note, U eid %s'
                            % self.user().eid)
         self.app.publish('view', req)
-        
+
     def test_publish_validation_error(self):
         req = self.request()
         user = self.user()
@@ -204,12 +210,12 @@
 
     def test_validation_error_dont_loose_subentity_data(self):
         """test creation of two linked entities
-        """        
+        """
         req = self.request()
         form = {'eid': ['X', 'Y'],
                 '__type:X': 'CWUser',
                 # missing required field
-                'login:X': u'', 'edits-login:X': '', 
+                'login:X': u'', 'edits-login:X': '',
                 'surname:X': u'Mr Ouaoua', 'edits-surname:X': '',
                 '__type:Y': 'EmailAddress',
                 # but email address is set
@@ -229,13 +235,13 @@
         self.assertEquals(forminfo['errors'].errors, {'login': 'required attribute',
                                                       'upassword': 'required attribute'})
         self.assertEquals(forminfo['values'], form)
-        
+
     def _test_cleaned(self, kwargs, injected, cleaned):
         req = self.request(**kwargs)
         page = self.app.publish('view', req)
         self.failIf(injected in page, (kwargs, injected))
         self.failUnless(cleaned in page, (kwargs, cleaned))
-        
+
     def test_nonregr_script_kiddies(self):
         """test against current script injection"""
         injected = '<i>toto</i>'
@@ -245,7 +251,7 @@
                        {'vtitle': injected},
                        ):
             yield self._test_cleaned, kwargs, injected, cleaned
-        
+
     def test_site_wide_eproperties_sync(self):
         # XXX work in all-in-one configuration but not in twisted for instance
         # in which case we need a kindof repo -> http server notification
@@ -278,7 +284,7 @@
         self.failIf(req.cnx is origcnx)
         self.assertEquals(req.user.login, 'turlututu')
         self.failUnless('turlututu' in page, page)
-        
+
     # authentication tests ####################################################
 
     def _init_auth(self, authmode, anonuser=None):
@@ -289,7 +295,7 @@
         req.cnx = None
         sh = self.app.session_handler
         # not properly cleaned between tests
-        self.open_sessions = sh.session_manager._sessions = {} 
+        self.open_sessions = sh.session_manager._sessions = {}
         return req, origcnx
 
     def _test_auth_succeed(self, req, origcnx):
@@ -299,16 +305,16 @@
         self.assertEquals(len(self.open_sessions), 1, self.open_sessions)
         self.assertEquals(cnx.login, origcnx.login)
         self.assertEquals(cnx.password, origcnx.password)
-        self.assertEquals(cnx.anonymous_connection, False) 
+        self.assertEquals(cnx.anonymous_connection, False)
         self.assertEquals(path, 'view')
         self.assertEquals(params, {'__message': 'welcome %s !' % origcnx.login})
-    
+
     def _test_auth_fail(self, req):
         self.assertRaises(AuthenticationError, self.app.connect, req)
         self.assertEquals(req.cnx, None)
-        self.assertEquals(len(self.open_sessions), 0) 
+        self.assertEquals(len(self.open_sessions), 0)
         clear_cache(req, 'get_authorization')
-        
+
     def test_http_auth_no_anon(self):
         req, origcnx = self._init_auth('http')
         self._test_auth_fail(req)
@@ -318,7 +324,7 @@
         req._headers['Authorization'] = 'basic %s' % authstr
         self._test_auth_succeed(req, origcnx)
         self.assertRaises(AuthenticationError, self.publish, req, 'logout')
-        self.assertEquals(len(self.open_sessions), 0) 
+        self.assertEquals(len(self.open_sessions), 0)
 
     def test_cookie_auth_no_anon(self):
         req, origcnx = self._init_auth('cookie')
@@ -331,7 +337,7 @@
         req.form['__password'] = origcnx.password
         self._test_auth_succeed(req, origcnx)
         self.assertRaises(AuthenticationError, self.publish, req, 'logout')
-        self.assertEquals(len(self.open_sessions), 0) 
+        self.assertEquals(len(self.open_sessions), 0)
 
     def test_login_by_email(self):
         login = self.request().user.login
@@ -351,17 +357,17 @@
         req.form['__password'] = origcnx.password
         self._test_auth_succeed(req, origcnx)
         self.assertRaises(AuthenticationError, self.publish, req, 'logout')
-        self.assertEquals(len(self.open_sessions), 0) 
+        self.assertEquals(len(self.open_sessions), 0)
 
     def _test_auth_anon(self, req):
         self.app.connect(req)
         acnx = req.cnx
         self.assertEquals(len(self.open_sessions), 1)
-        self.assertEquals(acnx.login, 'anon') 
-        self.assertEquals(acnx.password, 'anon') 
+        self.assertEquals(acnx.login, 'anon')
+        self.assertEquals(acnx.password, 'anon')
         self.failUnless(acnx.anonymous_connection)
         self._reset_cookie(req)
-        
+
     def _reset_cookie(self, req):
         # preparing the suite of the test
         # set session id in cookie
@@ -371,15 +377,15 @@
         clear_cache(req, 'get_authorization')
         # reset cnx as if it was a new incoming request
         req.cnx = None
-        
+
     def _test_anon_auth_fail(self, req):
-        self.assertEquals(len(self.open_sessions), 1) 
+        self.assertEquals(len(self.open_sessions), 1)
         self.app.connect(req)
         self.assertEquals(req.message, 'authentication failure')
         self.assertEquals(req.cnx.anonymous_connection, True)
-        self.assertEquals(len(self.open_sessions), 1) 
+        self.assertEquals(len(self.open_sessions), 1)
         self._reset_cookie(req)
-        
+
     def test_http_auth_anon_allowed(self):
         req, origcnx = self._init_auth('http', 'anon')
         self._test_auth_anon(req)
@@ -390,8 +396,8 @@
         req._headers['Authorization'] = 'basic %s' % authstr
         self._test_auth_succeed(req, origcnx)
         self.assertRaises(AuthenticationError, self.publish, req, 'logout')
-        self.assertEquals(len(self.open_sessions), 0) 
-        
+        self.assertEquals(len(self.open_sessions), 0)
+
     def test_cookie_auth_anon_allowed(self):
         req, origcnx = self._init_auth('cookie', 'anon')
         self._test_auth_anon(req)
@@ -402,7 +408,7 @@
         req.form['__password'] = origcnx.password
         self._test_auth_succeed(req, origcnx)
         self.assertRaises(AuthenticationError, self.publish, req, 'logout')
-        self.assertEquals(len(self.open_sessions), 0) 
+        self.assertEquals(len(self.open_sessions), 0)
 
 
 if __name__ == '__main__':
--- a/web/test/unittest_controller.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/unittest_controller.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,5 +1,6 @@
 """cubicweb.web.controller unit tests
 
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 from datetime import datetime, date, time
--- a/web/test/unittest_form.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/unittest_form.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,13 +1,22 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
+
 from logilab.common.testlib import unittest_main, mock_object
+
 from cubicweb import Binary
 from cubicweb.devtools.testlib import WebTest
-from cubicweb.web.form import EntityFieldsForm, FieldsForm
-from cubicweb.web.formrenderers import FormRenderer
 from cubicweb.web.formfields import (IntField, StringField, RichTextField,
                                      DateTimeField, DateTimePicker,
                                      FileField, EditableFileField)
 from cubicweb.web.formwidgets import PasswordInput
+from cubicweb.web.views.forms import EntityFieldsForm, FieldsForm
 from cubicweb.web.views.workflow import ChangeStateForm
+from cubicweb.web.views.formrenderers import FormRenderer
 
 
 class FieldsFormTC(WebTest):
@@ -26,7 +35,6 @@
         super(EntityFieldsFormTC, self).setUp()
         self.req = self.request()
         self.entity = self.user(self.req)
-        self.renderer = FormRenderer()
 
     def test_form_field_vocabulary_unrelated(self):
         b = self.add_entity('BlogEntry', title=u'di mascii code', content=u'a best-seller')
@@ -64,8 +72,30 @@
         self.assertEquals(len(states), 1)
         self.assertEquals(states[0][0], u'deactivated') # list of (combobox view, state eid)
 
+    def test_consider_req_form_params(self):
+        e = self.etype_instance('CWUser')
+        e.eid = 'A'
+        form = EntityFieldsForm(self.request(login=u'toto'), None, entity=e)
+        field = StringField(name='login', eidparam=True)
+        form.append_field(field)
+        form.form_build_context({})
+        self.assertEquals(form.form_field_display_value(field, {}), 'toto')
 
 
+    def test_linkto_field_duplication(self):
+        e = self.etype_instance('CWUser')
+        e.eid = 'A'
+        e.req = self.req
+        geid = self.execute('CWGroup X WHERE X name "users"')[0][0]
+        self.req.form['__linkto'] = 'in_group:%s:subject' % geid
+        form = self.vreg.select_object('forms', 'edition', self.req, None, entity=e)
+        form.content_type = 'text/html'
+        pageinfo = self._check_html(form.form_render(), form, template=None)
+        inputs = pageinfo.find_tag('select', False)
+        self.failUnless(any(attrs for t, attrs in inputs if attrs.get('name') == 'in_group:A'))
+        inputs = pageinfo.find_tag('input', False)
+        self.failIf(any(attrs for t, attrs in inputs if attrs.get('name') == '__linkto'))
+
     # form view tests #########################################################
 
     def test_massmailing_formview(self):
@@ -94,7 +124,8 @@
 
     def _render_entity_field(self, name, form):
         form.form_build_context({})
-        return form.field_by_name(name).render(form, self.renderer)
+        renderer = FormRenderer(self.req)
+        return form.field_by_name(name).render(form, renderer)
 
     def _test_richtextfield(self, expected):
         class RTFForm(EntityFieldsForm):
@@ -124,8 +155,8 @@
 
     def test_filefield(self):
         class FFForm(EntityFieldsForm):
-            data = FileField(format_field=StringField(name='data_format'),
-                             encoding_field=StringField(name='data_encoding'))
+            data = FileField(format_field=StringField(name='data_format', max_length=50),
+                             encoding_field=StringField(name='data_encoding', max_length=20))
         file = self.add_entity('File', name=u"pouet.txt", data_encoding=u'UTF-8',
                                data=Binary('new widgets system'))
         form = FFForm(self.req, redirect_path='perdu.com', entity=file)
@@ -133,8 +164,8 @@
                               '''<input id="data:%(eid)s" name="data:%(eid)s" tabindex="0" type="file" value=""/>
 <a href="javascript: toggleVisibility(&#39;data:%(eid)s-advanced&#39;)" title="show advanced fields"><img src="http://testing.fr/cubicweb/data/puce_down.png" alt="show advanced fields"/></a>
 <div id="data:%(eid)s-advanced" class="hidden">
-<label for="data_format:%(eid)s">data_format</label><input id="data_format:%(eid)s" name="data_format:%(eid)s" tabindex="1" type="text" value="text/plain"/><br/><br/>
-<label for="data_encoding:%(eid)s">data_encoding</label><input id="data_encoding:%(eid)s" name="data_encoding:%(eid)s" tabindex="2" type="text" value="UTF-8"/><br/><br/>
+<label for="data_format:%(eid)s">data_format</label><input id="data_format:%(eid)s" name="data_format:%(eid)s" tabindex="1" type="text" value="text/plain"/><br/>
+<label for="data_encoding:%(eid)s">data_encoding</label><input id="data_encoding:%(eid)s" name="data_encoding:%(eid)s" tabindex="2" type="text" value="UTF-8"/><br/>
 </div>
 <br/>
 <input name="data:%(eid)s__detach" type="checkbox"/>
@@ -144,8 +175,8 @@
 
     def test_editablefilefield(self):
         class EFFForm(EntityFieldsForm):
-            data = EditableFileField(format_field=StringField(name='data_format'),
-                                     encoding_field=StringField(name='data_encoding'))
+            data = EditableFileField(format_field=StringField(name='data_format', max_length=50),
+                                     encoding_field=StringField(name='data_encoding', max_length=20))
             def form_field_encoding(self, field):
                 return 'ascii'
             def form_field_format(self, field):
@@ -157,8 +188,8 @@
                               '''<input id="data:%(eid)s" name="data:%(eid)s" tabindex="0" type="file" value=""/>
 <a href="javascript: toggleVisibility(&#39;data:%(eid)s-advanced&#39;)" title="show advanced fields"><img src="http://testing.fr/cubicweb/data/puce_down.png" alt="show advanced fields"/></a>
 <div id="data:%(eid)s-advanced" class="hidden">
-<label for="data_format:%(eid)s">data_format</label><input id="data_format:%(eid)s" name="data_format:%(eid)s" tabindex="1" type="text" value="text/plain"/><br/><br/>
-<label for="data_encoding:%(eid)s">data_encoding</label><input id="data_encoding:%(eid)s" name="data_encoding:%(eid)s" tabindex="2" type="text" value="UTF-8"/><br/><br/>
+<label for="data_format:%(eid)s">data_format</label><input id="data_format:%(eid)s" name="data_format:%(eid)s" tabindex="1" type="text" value="text/plain"/><br/>
+<label for="data_encoding:%(eid)s">data_encoding</label><input id="data_encoding:%(eid)s" name="data_encoding:%(eid)s" tabindex="2" type="text" value="UTF-8"/><br/>
 </div>
 <br/>
 <input name="data:%(eid)s__detach" type="checkbox"/>
--- a/web/test/unittest_formfields.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/unittest_formfields.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,11 +1,21 @@
-"""unittests for cw.web.formfields"""
+"""unittests for cw.web.formfields
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 from logilab.common.testlib import TestCase, unittest_main
+
+from yams.constraints import StaticVocabularyConstraint, SizeConstraint
+
 from cubicweb.devtools import TestServerConfiguration
-from cubicweb.web.formwidgets import PasswordInput, TextArea
+from cubicweb.devtools.testlib import EnvBasedTC
+from cubicweb.web.formwidgets import PasswordInput, TextArea, Select
 from cubicweb.web.formfields import *
-from cubicweb.entities.wfobjs import State
-from cubicweb.entities.authobjs import CWUser
+from cubicweb.web.views.forms import EntityFieldsForm
+
 from cubes.file.entities import File
 
 config = TestServerConfiguration('data')
@@ -14,6 +24,7 @@
 state_schema = schema['State']
 cwuser_schema = schema['CWUser']
 file_schema = schema['File']
+salesterm_schema = schema['Salesterm']
 
 class GuessFieldTC(TestCase):
 
@@ -41,12 +52,13 @@
         self.assertEquals(description_format_field.sort, True)
         self.assertEquals(description_format_field.initial(None), 'text/rest')
 
+
 #         wikiid_field = guess_field(state_schema, schema['wikiid'])
 #         self.assertIsInstance(wikiid_field, StringField)
 #         self.assertEquals(wikiid_field.required, False)
 
 
-    def test_euser_fields(self):
+    def test_cwuser_fields(self):
         upassword_field = guess_field(cwuser_schema, schema['upassword'])
         self.assertIsInstance(upassword_field, StringField)
         self.assertIsInstance(upassword_field.widget, PasswordInput)
@@ -80,5 +92,28 @@
         self.assertIsInstance(data_field.format_field, StringField)
         self.assertIsInstance(data_field.encoding_field, StringField)
 
+    def test_constraints_priority(self):
+        salesterm_field = guess_field(salesterm_schema, schema['reason'])
+        constraints = schema['reason'].rproperty('Salesterm', 'String', 'constraints')
+        self.assertEquals([c.__class__ for c in constraints],
+                          [SizeConstraint, StaticVocabularyConstraint])
+        self.assertIsInstance(salesterm_field.widget, Select)
+
+class MoreFieldsTC(EnvBasedTC):
+    def test_rtf_format_field(self):
+        req = self.request()
+        req.use_fckeditor = lambda: False
+        e = self.etype_instance('State')
+        form = EntityFieldsForm(req, entity=e)
+        description_field = guess_field(state_schema, schema['description'])
+        description_format_field = description_field.get_format_field(form)
+        self.assertEquals(description_format_field.internationalizable, True)
+        self.assertEquals(description_format_field.sort, True)
+        # unlike below, initial is bound to form.form_field_format
+        self.assertEquals(description_format_field.initial(form), 'text/html')
+        self.execute('INSERT CWProperty X: X pkey "ui.default-text-format", X value "text/rest", X for_user U WHERE U login "admin"')
+        self.commit()
+        self.assertEquals(description_format_field.initial(form), 'text/rest')
+
 if __name__ == '__main__':
     unittest_main()
--- a/web/test/unittest_magicsearch.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/unittest_magicsearch.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,5 +1,11 @@
 # -*- coding: utf-8 -*-
-"""Unit tests for magic_search service"""
+"""Unit tests for magic_search service
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 import sys
 
--- a/web/test/unittest_urlpublisher.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/unittest_urlpublisher.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,5 +1,11 @@
 # -*- coding: utf-8 -*-
-"""Unit tests for url publishing service"""
+"""Unit tests for url publishing service
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 import re
 
--- a/web/test/unittest_urlrewrite.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/unittest_urlrewrite.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from logilab.common.testlib import TestCase, unittest_main
 
 from cubicweb.devtools._apptest import FakeRequest
@@ -23,8 +30,8 @@
             ('foo' , dict(rql='Foo F')),
             ('/index' , dict(vid='index2')),
             ('/schema', {'vid': 'schema'}),
-            ('/myprefs', dict(vid='epropertiesform')),
-            ('/siteconfig', dict(vid='systemepropertiesform')),
+            ('/myprefs', dict(vid='propertiesform')),
+            ('/siteconfig', dict(vid='systempropertiesform')),
             ('/manage', dict(vid='manage')),
             ('/notfound', {'vid': '404'}),
             ('/error', {'vid': 'error'}),
@@ -35,7 +42,7 @@
             ('/doc/(.+?)/?$', dict(fid='\\1', vid='wdoc')),
             ('/changelog/?$', dict(vid='changelog')),
             # now in SchemaBasedRewriter
-            #('/search/(.+)$', dict(rql=r'Any X WHERE X has_text "\1"')), 
+            #('/search/(.+)$', dict(rql=r'Any X WHERE X has_text "\1"')),
             ])
 
 
@@ -94,8 +101,8 @@
         pmid, rset = rewriter.rewrite(req, u'/DaLToN/JoE')
         self.assertEquals(len(rset), 1)
         self.assertEquals(rset[0][0], self.p1.eid)
-        
-    
+
+
 
 if __name__ == '__main__':
     unittest_main()
--- a/web/test/unittest_views_actions.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/unittest_views_actions.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from logilab.common.testlib import unittest_main
 
 from cubicweb.devtools.apptest import EnvBasedTC
@@ -20,6 +27,6 @@
         rset = self.execute('Any X WHERE X login "anon"', req=req)
         self.failIf([action for action in self.vreg.possible_vobjects('actions', req, rset)
                      if action.id == 'sendemail'])
-        
+
 if __name__ == '__main__':
     unittest_main()
--- a/web/test/unittest_views_apacherewrite.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/unittest_views_apacherewrite.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from logilab.common.testlib import TestCase, unittest_main
 
 from cubicweb.web.views.apacherewrite import *
@@ -16,22 +23,23 @@
                             rules=[('/(.*)', r'/m_%(cat)s/\1')]),
                 ]
         urlrewriter = MyAppRules()
+        req = None # not used in the above rules, so keep a simple TestCase here
         try:
-            urlrewriter.rewrite('logilab.fr', '/whatever')
+            urlrewriter.rewrite('logilab.fr', '/whatever', req)
             self.fail('redirect exception expected')
         except Redirect, ex:
             self.assertEquals(ex.location, 'http://www.logilab.fr/whatever')
-        self.assertEquals(urlrewriter.rewrite('www.logilab.fr', '/whatever'),
+        self.assertEquals(urlrewriter.rewrite('www.logilab.fr', '/whatever', req),
                           '/whatever')
-        self.assertEquals(urlrewriter.rewrite('www.logilab.fr', '/json/bla'),
+        self.assertEquals(urlrewriter.rewrite('www.logilab.fr', '/json/bla', req),
                           '/json/bla')
-        self.assertEquals(urlrewriter.rewrite('abcd.logilab.fr', '/json/bla'),
+        self.assertEquals(urlrewriter.rewrite('abcd.logilab.fr', '/json/bla', req),
                           '/json/bla')
-        self.assertEquals(urlrewriter.rewrite('abcd.logilab.fr', '/data/bla'),
+        self.assertEquals(urlrewriter.rewrite('abcd.logilab.fr', '/data/bla', req),
                           '/data/bla')
-        self.assertEquals(urlrewriter.rewrite('abcd.logilab.fr', '/whatever'),
+        self.assertEquals(urlrewriter.rewrite('abcd.logilab.fr', '/whatever', req),
                           '/m_abcd/whatever')
-        self.assertEquals(urlrewriter.rewrite('abcd.fr', '/whatever'),
+        self.assertEquals(urlrewriter.rewrite('abcd.fr', '/whatever', req),
                           '/whatever')
 
 
--- a/web/test/unittest_views_basecontrollers.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/unittest_views_basecontrollers.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,7 +1,13 @@
-"""cubicweb.web.views.basecontrollers unit tests"""
+"""cubicweb.web.views.basecontrollers unit tests
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 import simplejson
 
-from logilab.common.testlib import unittest_main
+from logilab.common.testlib import unittest_main, mock_object
 
 from cubicweb import Binary, Unauthorized
 from cubicweb.devtools._apptest import TestEnvironment
@@ -533,7 +539,7 @@
         rset = self.john.as_rset()
         rset.req = req
         self.assertTextEquals(ctrl.publish(),
-                              xhtml_wrap(ctrl.view('primary', rset)))
+                              xhtml_wrap(mock_object(req=req), ctrl.view('primary', rset)))
 
 #     def test_json_exec(self):
 #         rql = 'Any T,N WHERE T is Tag, T name N'
@@ -562,7 +568,7 @@
                          ('eid', 'firstname:%s' % eid, '__maineid', '__type:%s'% eid, 'edits-firstname:%s' % eid ),
                          (str(eid), u'Remi', str(eid), 'CWUser', self.john.firstname),
                          'firstname',
-                         eid)
+                         eid, 'default_value')
         self.commit()
         rset = self.execute('CWUser P')
         # make sure we did not insert a new cwuser here
--- a/web/test/unittest_views_baseforms.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/unittest_views_baseforms.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""cubicweb.web.views.baseforms unit tests"""
+"""cubicweb.web.views.baseforms unit tests
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 from StringIO import StringIO
 from datetime import date
@@ -31,7 +37,7 @@
 
     def setup_database(self):
         self.create_user('joe')
-        
+
     def _build_creation_form(self, etype):
         req = self.request()
         req.next_tabindex()
@@ -44,10 +50,10 @@
         view.w = buffer.write
         view.edit_form(entity, {})
         return buffer.getvalue()
-    
+
     def _test_view_for(self, etype, expected):
         self.assertTextEquals(expected, cleanup_text(self._build_creation_form(etype)))
-        
+
     def test_base(self):
         self._test_view_for('CWGroup', '''\
 <form id="entityForm" class="entityForm" cubicweb:target="eformframe"
@@ -198,7 +204,7 @@
         entity = vreg.etype_class('Salesterm')(req, None, None)
         view = vreg.select_view('creation', req, None)
         self.failUnless(view.need_multipart(entity))
-        
+
 
 
     def test_nonregr_check_add_permission_on_relation(self):
@@ -214,7 +220,7 @@
         self.login('joe')
         html = self._build_creation_form('BlogEntry')
         self.failIf('name="edits-checked_by:A"' in html)
-        
+
 from cubicweb.devtools.testlib import WebTest
 from cubicweb.devtools.htmlparser import DTDValidator
 
--- a/web/test/unittest_views_basetemplates.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/unittest_views_basetemplates.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from cubicweb.devtools.testlib import WebTest
 from cubicweb.devtools.htmlparser import DTDValidator
 
--- a/web/test/unittest_views_baseviews.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/unittest_views_baseviews.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from simplejson import loads
 
 from logilab.common.testlib import unittest_main
--- a/web/test/unittest_views_editforms.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/unittest_views_editforms.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from logilab.common.testlib import unittest_main
 from cubicweb.devtools.apptest import EnvBasedTC
 from cubicweb.devtools.testlib import WebTest
--- a/web/test/unittest_views_embeding.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/unittest_views_embeding.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 from logilab.common.testlib import TestCase, unittest_main
 
--- a/web/test/unittest_views_navigation.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/unittest_views_navigation.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,4 +1,10 @@
-"""cubicweb.web.views.navigation unit tests"""
+"""cubicweb.web.views.navigation unit tests
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 
 from logilab.common.testlib import unittest_main, mock_object
 from cubicweb.devtools.apptest import EnvBasedTC
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/test/unittest_views_pyviews.py	Wed Jun 03 09:15:20 2009 +0200
@@ -0,0 +1,25 @@
+from logilab.common.testlib import unittest_main
+from cubicweb.devtools.apptest import EnvBasedTC
+
+class PyViewsTC(EnvBasedTC):
+
+    def test_pyvaltable(self):
+        content = self.vreg.view('pyvaltable', self.request(),
+                                 pyvalue=[[1, 'a'], [2, 'b']],
+                                 headers=['num', 'char'])
+        self.assertEquals(content.strip(), '''<table class="listing">
+<tr><th>num</th><th>char</th></tr>
+<tr><td>1</td><td>a</td></tr>
+<tr><td>2</td><td>b</td></tr>
+</table>''')
+
+    def test_pyvallist(self):
+        content = self.vreg.view('pyvallist', self.request(),
+                                 pyvalue=[1, 'a'])
+        self.assertEquals(content.strip(), '''<ul>
+<li>1</li>
+<li>a</li>
+</ul>''')
+
+if __name__ == '__main__':
+    unittest_main()
--- a/web/test/unittest_views_searchrestriction.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/unittest_views_searchrestriction.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from cubicweb.devtools.apptest import EnvBasedTC
 from cubicweb.web.facet import insert_attr_select_relation, prepare_facets_rqlst
 
@@ -15,7 +22,7 @@
         mainvar = prepare_facets_rqlst(rqlst)[0]
         insert_attr_select_relation(rqlst.children[0], mainvar, rel, role, attr)
         return rqlst.as_string()
-        
+
     @property
     def select(self):
         return self.parse('Any B,(NOW - CD),S,V,U,GROUP_CONCAT(TN),VN,P,CD,BMD '
@@ -24,30 +31,30 @@
                           'B modification_date BMD, T? tags B, T name TN, '
                           'V? bookmarked_by B, V title VN, B created_by U?, '
                           'B in_group P, P name "managers"')
-    
+
     def test_1(self):
         self.assertEquals(self._generate(self.select, 'in_state', 'subject', 'name'),
                           "DISTINCT Any A,C ORDERBY C WHERE B in_group P, P name 'managers', "
                           "B in_state A, A name C, B is CWUser")
-        
+
     def test_2(self):
         self.assertEquals(self._generate(self.select, 'tags', 'object', 'name'),
                           "DISTINCT Any A,C ORDERBY C WHERE B in_group P, P name 'managers', "
                           "A tags B, A name C, B is CWUser")
-        
+
     def test_3(self):
         self.assertEquals(self._generate(self.select, 'created_by', 'subject', 'login'),
                           "DISTINCT Any A,C ORDERBY C WHERE B in_group P, P name 'managers', "
                           "B created_by A, A login C, B is CWUser")
-        
+
     def test_4(self):
         self.assertEquals(self._generate(self.parse('Any X WHERE X is CWUser'), 'created_by', 'subject', 'login'),
                           "DISTINCT Any A,B ORDERBY B WHERE X is CWUser, X created_by A, A login B")
-        
+
     def test_5(self):
         self.assertEquals(self._generate(self.parse('Any X,L WHERE X is CWUser, X login L'), 'created_by', 'subject', 'login'),
                           "DISTINCT Any A,B ORDERBY B WHERE X is CWUser, X created_by A, A login B")
-        
+
     def test_nonregr1(self):
         select = self.parse('Any T,V WHERE T bookmarked_by V?, '
                             'V in_state VS, VS name "published", T created_by U')
@@ -77,7 +84,7 @@
         self.assertEquals(self._generate(select, 'in_group', 'subject', 'name'),
                           "DISTINCT Any B,C ORDERBY C WHERE X is CWUser, X in_group B, B name C")
 
-        
+
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main
     unittest_main()
--- a/web/test/unittest_viewselector.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/unittest_viewselector.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,7 @@
 # -*- coding: iso-8859-1 -*-
 """XXX rename, split, reorganize this
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
-from __future__ import with_statement
-
 from logilab.common.testlib import unittest_main
 
 from cubicweb.devtools.apptest import EnvBasedTC
@@ -66,13 +65,13 @@
         self.assertListEqual(self.pviews(req, None),
                              [('changelog', wdoc.ChangeLogView),
                               ('debug', debug.DebugView),
-                              ('epropertiesform', cwproperties.EPropertiesForm),
                               ('index', startup.IndexView),
                               ('info', management.ProcessInformationView),
                               ('manage', startup.ManageView),
                               ('owl', owl.OWLView),
+                              ('propertiesform', cwproperties.CWPropertiesForm),
                               ('schema', startup.SchemaView),
-                              ('systemepropertiesform', cwproperties.SystemEPropertiesForm)])
+                              ('systempropertiesform', cwproperties.SystemCWPropertiesForm)])
 
     def test_possible_views_noresult(self):
         rset, req = self.env.get_rset_and_req('Any X WHERE X eid 999999')
--- a/web/test/unittest_web.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/unittest_web.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 from logilab.common.testlib import TestCase, unittest_main
 from cubicweb.web import ajax_replace_url as  arurl
 class AjaxReplaceUrlTC(TestCase):
--- a/web/test/unittest_webconfig.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/test/unittest_webconfig.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,3 +1,10 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
 import os
 
 from logilab.common.testlib import TestCase, unittest_main
@@ -10,7 +17,7 @@
         self.config = ApptestConfiguration('data')
         self.config._cubes = ['file']
         self.config.load_configuration()
-        
+
     def test_nonregr_print_css_as_list(self):
         """make sure PRINT_CSS *must* is a list"""
         config = self.config
@@ -26,8 +33,8 @@
         self.failUnless('file' in self.config.locate_resource(rname).split(os.sep))
         cubicwebcsspath = self.config.locate_resource('cubicweb.css').split(os.sep)
         self.failUnless('web' in cubicwebcsspath or 'shared' in cubicwebcsspath) # 'shared' if tests under apycot
-        
+
 if __name__ == '__main__':
     unittest_main()
 
-    
+
--- a/web/test/unittest_widgets.py	Wed Jun 03 09:09:33 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,371 +0,0 @@
-"""cubicweb.common.widget unit tests
-
-"""
-
-from datetime import datetime
-NOW = datetime.now()
-
-from logilab.common.testlib import unittest_main
-from cubicweb.devtools.apptest import EnvBasedTC
-
-from cubicweb.common.mttransforms import HAS_TAL
-from cubicweb.web.widgets import widget, AutoCompletionWidget
-
-
-class WidgetsTC(EnvBasedTC):
-        
-    def get_widget(self, etype, rname, rtype):
-        rschema = self.schema[rname]
-        return widget(self.vreg, etype, rschema, rtype, role='subject')
-    
-
-    def test_hidden_widget(self):
-        w = self.get_widget('State', 'eid', 'Int')
-        self.assertEquals(w.name, 'eid')
-        self.assertEquals(w.render_example(self.request()), '')
-        self.assertDictEquals(w.attrs, {})
-        entity = self.etype_instance('State')
-        entity.eid = 'X'
-        self.assertEquals(w.required(entity), True)
-        self.assertEquals(w.render(entity), '')
-        self.assertEquals(w.edit_render(entity),
-                           u'<input type="hidden" name="eid" value="X" />')
-
-    def test_textarea_widget(self):
-        self.add_entity('CWProperty', pkey=u'ui.fckeditor', value=u'')
-        self.commit()
-        w = self.get_widget('State', 'description', 'String')
-        self.assertEquals(w.name, 'description')
-        self.assertEquals(w.render_example(self.request()), '')
-        self.assertDictEquals(w.attrs, {'accesskey': 'd'})
-        entity = self.etype_instance('State')
-        entity.eid = 'X'
-        entity
-        self.assertEquals(w.required(entity), False)
-        self.assertEquals(w.render(entity), '')
-        if HAS_TAL:
-            tal_format = u'\n<option value="text/cubicweb-page-template" >text/cubicweb-page-template</option>'
-        else:
-            tal_format = u''
-        self.assertTextEquals(w.edit_render(entity),
-                           u'''<input type="hidden" name="edits-description:X" value="__cubicweb_internal_field__"/>
-<input type="hidden" name="edits-description_format:X" value="__cubicweb_internal_field__"/>
-
-<select name="description_format:X" id="description_format:X" tabindex="0">
-<option value="text/rest" >text/rest</option>
-<option value="text/html" selected="selected">text/html</option>
-<option value="text/plain" >text/plain</option>%s
-</select><br/><textarea onkeypress="autogrow(this)" name="description:X" accesskey="d" cols="80" id="description:X" rows="20" tabindex="1"></textarea>''' % tal_format)
-
-    def test_textarea_widget_previous_value(self):
-        self.add_entity('CWProperty', pkey=u'ui.fckeditor', value=u'')
-        self.commit()
-        w = self.get_widget('State', 'description', 'String')
-        req = self.request()
-        req.data['formvalues'] = {'description:X': 'a description'}
-        entity = self.etype_instance('State', req)
-        entity.eid = 'X'
-        self.assertEquals(w.required(entity), False)
-        self.assertEquals(w.render(entity), '')
-        if HAS_TAL:
-            tal_format = u'\n<option value="text/cubicweb-page-template" >text/cubicweb-page-template</option>'
-        else:
-            tal_format = u''
-        self.assertTextEquals(w.edit_render(entity),
-                           u'''<input type="hidden" name="edits-description:X" value="__cubicweb_internal_field__"/>
-<input type="hidden" name="edits-description_format:X" value="__cubicweb_internal_field__"/>
-
-<select name="description_format:X" id="description_format:X" tabindex="0">
-<option value="text/rest" >text/rest</option>
-<option value="text/html" selected="selected">text/html</option>
-<option value="text/plain" >text/plain</option>%s
-</select><br/><textarea onkeypress="autogrow(this)" name="description:X" accesskey="d" cols="80" id="description:X" rows="20" tabindex="1">a description</textarea>''' % tal_format)
-
-    def test_fckeditor_widget(self):
-        w = self.get_widget('State', 'description', 'String')
-        req = self.request()
-        entity = self.etype_instance('State', req)
-        entity.eid = 'X'
-        self.assertEquals(w.required(entity), False)
-        self.assertEquals(w.render(entity), '')
-        self.assertTextEquals(w.edit_render(entity),
-                           u'''<input type="hidden" name="edits-description:X" value="__cubicweb_internal_field__"/>
-<input type="hidden" name="edits-description_format:X" value=""/>
-<input type="hidden" name="description_format:X" value="text/html"/>
-<textarea cubicweb:type="wysiwyg" onkeypress="autogrow(this)" name="description:X" accesskey="d" cols="80" id="description:X" rows="20" tabindex="0"></textarea>''')
-
-    def test_string_widget(self):
-        w = self.get_widget('Personne', 'nom', 'String')
-        self.assertEquals(w.name, 'nom')
-        self.assertEquals(w.render_example(self.request()), '')
-        self.assertDictEquals(w.attrs, {'accesskey': 'n', 'maxlength': 64, 'size': 40})
-        entity = self.etype_instance('Personne')
-        entity.eid = 'X'
-        self.assertEquals(w.required(entity), True)
-        self.assertEquals(w.render(entity), '')
-        self.assertEquals(w.edit_render(entity),
-                           u'<input type="hidden" name="edits-nom:X" value="__cubicweb_internal_field__"/>\n'
-                          '<input type="text" name="nom:X" value="" accesskey="n" id="nom:X" maxlength="64" size="40" tabindex="0"/>')
-
-    def test_string_widget_previous_value(self):
-        w = self.get_widget('Personne', 'nom', 'String')
-        self.assertEquals(w.name, 'nom')
-        self.assertEquals(w.render_example(self.request()), '')
-        self.assertDictEquals(w.attrs, {'accesskey': 'n', 'maxlength': 64, 'size': 40})
-        req = self.request()
-        req.data['formvalues'] = {'nom:X': 'a name'}
-        entity = self.etype_instance('Personne', req)
-        entity.eid = 'X'
-        self.assertEquals(w.required(entity), True)
-        self.assertEquals(w.render(entity), '')
-        self.assertEquals(w.edit_render(entity),
-                           u'<input type="hidden" name="edits-nom:X" value="__cubicweb_internal_field__"/>\n'
-                          '<input type="text" name="nom:X" value="a name" accesskey="n" id="nom:X" maxlength="64" size="40" tabindex="0"/>')
-
-    def test_static_combo_widget(self):
-        w = self.get_widget('Personne', 'promo', 'String')
-        self.assertEquals(w.name, 'promo')
-        self.assertEquals(w.render_example(self.request()), '')
-        self.assertDictEquals(w.attrs, {})
-        entity = self.etype_instance('Personne')
-        entity.eid = 'X'
-        self.assertEquals(w.required(entity), False)
-        self.assertEquals(w.render(entity), '')
-        self.assertTextEquals(w.edit_render(entity),
-                           u'<input type="hidden" name="edits-promo:X" value="__cubicweb_internal_field__"/>\n\n'
-                          '<select name="promo:X" id="promo:X" tabindex="0">\n'
-                          '<option value="bon" >bon</option>\n'
-                          '<option value="pasbon" >pasbon</option>\n'
-                          '</select>')
-
-    def test_static_combo_widget_previous_value(self):
-        w = self.get_widget('Personne', 'promo', 'String')
-        self.assertEquals(w.name, 'promo')
-        self.assertEquals(w.render_example(self.request()), '')
-        self.assertDictEquals(w.attrs, {})
-        req = self.request()
-        req.data['formvalues'] = {'promo:X': 'pasbon'}
-        entity = self.etype_instance('Personne', req)
-        entity.eid = 'X'
-        self.assertEquals(w.required(entity), False)
-        self.assertEquals(w.render(entity), '')
-        self.assertTextEquals(w.edit_render(entity),
-                           u'<input type="hidden" name="edits-promo:X" value="__cubicweb_internal_field__"/>\n\n'
-                          '<select name="promo:X" id="promo:X" tabindex="0">\n'
-                          '<option value="bon" >bon</option>\n'
-                          '<option value="pasbon" selected="selected">pasbon</option>\n'
-                          '</select>')
-
-    def test_integer_widget(self):
-        w = self.get_widget('Personne', 'tel', 'Int')
-        self.assertEquals(w.name, 'tel')
-        self.assertEquals(w.render_example(self.request()), '23')
-        self.assertDictEquals(w.attrs, {'accesskey': 't', 'maxlength': 15, 'size': 5})
-        entity = self.etype_instance('Personne')
-        entity.eid = 'X'
-        self.assertEquals(w.required(entity), False)
-        self.assertEquals(w.render(entity), '')
-        self.assertEquals(w.edit_render(entity),
-                           u'<input type="hidden" name="edits-tel:X" value="__cubicweb_internal_field__"/>\n'
-                          '<input type="text" name="tel:X" value="" accesskey="t" id="tel:X" maxlength="15" size="5" tabindex="0"/>')
-
-    def test_integer_widget_previous_value(self):
-        w = self.get_widget('Personne', 'tel', 'Int')
-        self.assertEquals(w.name, 'tel')
-        self.assertEquals(w.render_example(self.request()), '23')
-        self.assertDictEquals(w.attrs, {'accesskey': 't', 'maxlength': 15, 'size': 5})
-        req = self.request()
-        req.data['formvalues'] = {'tel:X': '0123456789'}
-        entity = self.etype_instance('Personne', req)
-        entity.eid = 'X'
-        self.assertEquals(w.required(entity), False)
-        self.assertEquals(w.render(entity), '')
-        self.assertEquals(w.edit_render(entity),
-                           u'<input type="hidden" name="edits-tel:X" value="__cubicweb_internal_field__"/>\n'
-                          '<input type="text" name="tel:X" value="0123456789" accesskey="t" id="tel:X" maxlength="15" size="5" tabindex="0"/>')
-
-    def test_datetime_widget(self):
-        w = self.get_widget('Personne', 'datenaiss', 'Datetime')
-        self.assertEquals(w.name, 'datenaiss')
-        example = '%s, or without time: %s' % (        
-            NOW.strftime(self.vreg.property_value('ui.datetime-format')),
-            NOW.strftime(self.vreg.property_value('ui.date-format')))
-        self.assertEquals(w.render_example(self.request()), example)
-        self.assertDictEquals(w.attrs, {'accesskey': 'd', 'maxlength': 16, 'size': 16})
-        entity = self.etype_instance('Personne')
-        entity.eid = 'X'
-        self.assertEquals(w.required(entity), False)
-        self.assertEquals(w.render(entity), '')
-        self.assertEquals(w.edit_render(entity),
-                           u'<input type="hidden" name="edits-datenaiss:X" value="__cubicweb_internal_field__"/>\n'
-                          '<input type="text" name="datenaiss:X" value="" accesskey="d" id="datenaiss:X" maxlength="16" size="16" tabindex="0"/>'
-                          '<a onclick="toggleCalendar(\'datenaiss:Xhelper\', \'datenaiss:X\', %s, %s);" class="calhelper">\n<img src="http://testing.fr/cubicweb/data/calendar.gif" title="calendar" alt="" /></a><div class="calpopup hidden" id="datenaiss:Xhelper"></div>' % (NOW.year, NOW.month))
-
-    def test_datetime_widget_previous_value(self):
-        w = self.get_widget('Personne', 'datenaiss', 'Datetime')
-        self.assertEquals(w.name, 'datenaiss')
-        self.assertDictEquals(w.attrs, {'accesskey': 'd', 'maxlength': 16, 'size': 16})
-        req = self.request()
-        req.data['formvalues'] = {'datenaiss:X': '2000/01/01'}
-        entity = self.etype_instance('Personne', req)
-        entity.eid = 'X'
-        self.assertEquals(w.required(entity), False)
-        self.assertEquals(w.render(entity), '')
-        self.assertEquals(w.edit_render(entity),
-                           u'<input type="hidden" name="edits-datenaiss:X" value="__cubicweb_internal_field__"/>\n'
-                          '<input type="text" name="datenaiss:X" value="2000/01/01" accesskey="d" id="datenaiss:X" maxlength="16" size="16" tabindex="0"/>'
-                          '<a onclick="toggleCalendar(\'datenaiss:Xhelper\', \'datenaiss:X\', %s, %s);" class="calhelper">\n<img src="http://testing.fr/cubicweb/data/calendar.gif" title="calendar" alt="" /></a><div class="calpopup hidden" id="datenaiss:Xhelper"></div>' % (NOW.year, NOW.month))
-
-
-
-    def test_float_widget(self):
-        w = self.get_widget('Personne', 'salary', 'Float')
-        self.assertEquals(w.name, 'salary')
-        format = self.vreg.property_value('ui.float-format')
-        self.assertEquals(w.render_example(self.request()), format % 1.23)
-        self.assertDictEquals(w.attrs, {'accesskey': 's', 'maxlength': 15, 'size': 5})
-        entity = self.etype_instance('Personne')
-        entity.eid = 'X'
-        self.assertEquals(w.required(entity), False)
-        self.assertEquals(w.render(entity), '')
-        self.assertEquals(w.edit_render(entity),
-                          u'<input type="hidden" name="edits-salary:X" value="__cubicweb_internal_field__"/>\n'
-                          '<input type="text" name="salary:X" value="" accesskey="s" id="salary:X" maxlength="15" size="5" tabindex="0"/>')
-                          
-                          
-    def test_float_widget_previous_value(self):
-        w = self.get_widget('Personne', 'salary', 'Float')
-        self.assertEquals(w.name, 'salary')
-        format = self.vreg.property_value('ui.float-format')
-        self.assertEquals(w.render_example(self.request()), format % 1.23)
-        self.assertDictEquals(w.attrs, {'accesskey': 's', 'maxlength': 15, 'size': 5})
-        req = self.request()
-        req.data['formvalues'] = {'salary:X': 7.89}
-        entity = self.etype_instance('Personne', req)
-        entity.eid = 'X'
-        self.assertEquals(w.required(entity), False)
-        self.assertEquals(w.render(entity), '')
-        self.assertEquals(w.edit_render(entity),
-                          u'<input type="hidden" name="edits-salary:X" value="__cubicweb_internal_field__"/>\n'
-                          '<input type="text" name="salary:X" value="7.89" accesskey="s" id="salary:X" maxlength="15" size="5" tabindex="0"/>')
-
-
-    def test_bool_widget(self):
-        w = self.get_widget('Personne', 'test', 'Boolean')
-        self.assertEquals(w.name, 'test')
-        self.assertEquals(w.render_example(self.request()), '')
-        self.assertDictEquals(w.attrs, {'accesskey': 't'})
-        entity = self.etype_instance('Personne')
-        entity.eid = 'X'
-        self.assertEquals(w.required(entity), False)
-        self.assertEquals(w.render(entity), '')
-        self.assertEquals(w.edit_render(entity),
-                           u'''<input type="hidden" name="edits-test:X" value="__cubicweb_internal_field__"/>
-
-<input type="radio" name="test:X" value="1" accesskey="t" id="test:X" tabindex="0"/>yes<br/>
-<input type="radio" name="test:X" value="" accesskey="t" tabindex="0" checked="checked"/>no<br/>''')
-
-    def test_bool_widget_previous_value(self):
-        w = self.get_widget('Personne', 'test', 'Boolean')
-        self.assertEquals(w.name, 'test')
-        self.assertEquals(w.render_example(self.request()), '')
-        self.assertDictEquals(w.attrs, {'accesskey': 't'})
-        req = self.request()
-        req.data['formvalues'] = {'test:X': 'checked'}
-        entity = self.etype_instance('Personne', req)
-        entity.eid = 'X'
-        self.assertEquals(w.required(entity), False)
-        self.assertEquals(w.render(entity), '')
-        self.assertEquals(w.edit_render(entity),
-                           u'''<input type="hidden" name="edits-test:X" value="__cubicweb_internal_field__"/>
-
-<input type="radio" name="test:X" value="1" accesskey="t" id="test:X" tabindex="0" checked="checked"/>yes<br/>
-<input type="radio" name="test:X" value="" accesskey="t" tabindex="0"/>no<br/>''')
-
-
-    def test_password_widget(self):
-        w = self.get_widget('CWUser', 'upassword', 'Password')
-        self.assertEquals(w.name, 'upassword')
-        self.assertEquals(w.render_example(self.request()), '')
-        self.assertDictEquals(w.attrs, {'accesskey': 'u'})
-        entity = self.etype_instance('CWUser')
-        entity.eid = 'X'
-        self.assertEquals(w.required(entity), True)
-        self.assertEquals(w.render(entity), '')
-        self.assertEquals(w.edit_render(entity),
-                           u'<input type="hidden" name="edits-upassword:X" value="__cubicweb_internal_field__"/>\n'
-                          '<input type="password" name="upassword:X" value="" accesskey="u" id="upassword:X" tabindex="0"/><br/>\n'
-                          '<input type="password" name="upassword-confirm:X" id="upassword-confirm:X" tabindex="1"/>&nbsp;<span class="emphasis">(confirm password)</span>')
-
-    def test_autocompletion_widget(self):
-        entity = self.etype_instance('Personne')
-        entity.widgets['nom'] = 'AutoCompletionWidget'
-        entity.autocomplete_initfuncs = {'nom' : 'getnames'}
-        try:
-            w = self.get_widget(entity, 'nom', 'String')
-            self.failUnless(isinstance(w, AutoCompletionWidget))
-            self.assertEquals(w.name, 'nom')
-            self.assertEquals(w.render_example(self.request()), '')
-            self.assertDictEquals(w.attrs, {'accesskey': 'n', 'maxlength': 64, 'size': 40})
-            entity.eid = 'X'
-            self.assertEquals(w.required(entity), True)
-            self.assertEquals(w.render(entity), '')
-
-            self.assertTextEquals(w.edit_render(entity),
-                                  u'<input type="hidden" name="edits-nom:X" value="__cubicweb_internal_field__"/>\n'
-                                  u'<input type="text" name="nom:X" value="" cubicweb:dataurl="http://testing.fr/cubicweb/json?pageid=None&amp;mode=remote&amp;fname=getnames" class="widget required" id="nom:X" tabindex="0" cubicweb:loadtype="auto" cubicweb:wdgtype="SuggestField"  cubicweb:accesskey="n" cubicweb:maxlength="64" cubicweb:size="40" />')
-                                  
-        finally:
-            del entity.widgets['nom']
-
-
-    def test_autocompletion_widget_previous_value(self):
-        req = self.request()
-        req.data['formvalues'] = {'nom:X': 'a name'}
-        entity = self.etype_instance('Personne', req)
-        entity.widgets['nom'] = 'AutoCompletionWidget'
-        entity.autocomplete_initfuncs = {'nom' : 'getnames'}
-        try:
-            w = self.get_widget(entity, 'nom', 'String')
-            self.failUnless(isinstance(w, AutoCompletionWidget))
-            self.assertEquals(w.name, 'nom')
-            self.assertEquals(w.render_example(self.request()), '')
-            self.assertDictEquals(w.attrs, {'accesskey': 'n', 'maxlength': 64, 'size': 40})
-            entity.eid = 'X'
-            self.assertEquals(w.required(entity), True)
-            self.assertEquals(w.render(entity), '')
-            self.assertTextEquals(w.edit_render(entity),
-                                  u'<input type="hidden" name="edits-nom:X" value="__cubicweb_internal_field__"/>\n'
-                                  u'<input type="text" name="nom:X" value="a name" cubicweb:dataurl="http://testing.fr/cubicweb/json?pageid=None&amp;mode=remote&amp;fname=getnames" class="widget required" id="nom:X" tabindex="0" cubicweb:loadtype="auto" cubicweb:wdgtype="SuggestField"  cubicweb:accesskey="n" cubicweb:maxlength="64" cubicweb:size="40" />')
-            
-        finally:
-            del entity.widgets['nom']
-
-
-    def test_nonregr_float_widget_with_none(self):
-        w = self.get_widget('Personne', 'salary', 'Float')
-        self.assertEquals(w.name, 'salary')
-        format = self.vreg.property_value('ui.float-format')
-        self.assertEquals(w.render_example(self.request()), format % 1.23)
-        self.assertDictEquals(w.attrs, {'accesskey': 's', 'maxlength': 15, 'size': 5})
-        req = self.request()
-        entity = self.etype_instance('Personne', req)
-        entity.eid = 'X'
-        entity.salary = None
-        self.assertEquals(w.required(entity), False)
-        self.assertEquals(w.render(entity), '')
-        self.assertEquals(w.edit_render(entity),
-                          u'<input type="hidden" name="edits-salary:X" value="__cubicweb_internal_field__"/>\n'
-                          '<input type="text" name="salary:X" value="" accesskey="s" id="salary:X" maxlength="15" size="5" tabindex="0"/>')
-
-
-    def test_custom_widget_for_non_final_relation(self):
-        entity = self.etype_instance('Personne', self.request())
-        entity.widgets['travaille'] = 'AutoCompletionWidget'
-        entity.autocomplete_initfuncs = {'nom' : 'getnames'}
-        w = self.get_widget(entity, 'travaille', 'Societe')
-        self.failUnless(isinstance(w, AutoCompletionWidget))
-        
-        
-if __name__ == '__main__':
-    unittest_main()
--- a/web/uicfg.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/uicfg.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,6 +1,3 @@
-# :organization: Logilab
-# :copyright: 2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """This module regroups a set of structures that may be used to configure
 various places of the generated web interface.
 
@@ -63,16 +60,17 @@
 Automatic form configuration
 ````````````````````````````
 
+:organization: Logilab
+:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
+from cubicweb import neg_role
 from cubicweb.rtags import RelationTags, RelationTagsBool, RelationTagsSet
 from cubicweb.web import formwidgets
 
-# primary view configuration ##################################################
-
-def dual_role(role):
-    return 'object' if role == 'subject' else 'subject'
 
 def card_from_role(card, role):
     if role == 'subject':
@@ -80,10 +78,12 @@
     assert role in ('object', 'sobject'), repr(role)
     return card[1]
 
+# primary view configuration ##################################################
+
 def init_primaryview_section(rtag, sschema, rschema, oschema, role):
     if rtag.get(sschema, rschema, oschema, role) is None:
         card = card_from_role(rschema.rproperty(sschema, oschema, 'cardinality'), role)
-        composed = rschema.rproperty(sschema, oschema, 'composite') == dual_role(role)
+        composed = rschema.rproperty(sschema, oschema, 'composite') == neg_role(role)
         if rschema.is_final():
             if rschema.meta or oschema.type in ('Password', 'Bytes'):
                 section = 'hidden'
@@ -97,8 +97,9 @@
             section = 'sideboxes'
         rtag.tag_relation((sschema, rschema, oschema, role), section)
 
-primaryview_section = RelationTags(init_primaryview_section,
-                                    frozenset(('attributes', 'relations',
+primaryview_section = RelationTags('primaryview_section',
+                                   init_primaryview_section,
+                                   frozenset(('attributes', 'relations',
                                                'sideboxes', 'hidden')))
 for rtype in ('eid', 'creation_date', 'modification_date',
               'is', 'is_instance_of', 'identity',
@@ -108,6 +109,8 @@
               'see_also'):
     primaryview_section.tag_subject_of(('*', rtype, '*'), 'hidden')
     primaryview_section.tag_object_of(('*', rtype, '*'), 'hidden')
+primaryview_section.tag_subject_of(('*', 'use_email', '*'), 'attributes')
+primaryview_section.tag_subject_of(('*', 'primary_email', '*'), 'hidden')
 
 for attr in ('name', 'meta', 'final'):
     primaryview_section.tag_attribute(('CWEType', attr), 'hidden')
@@ -140,7 +143,8 @@
         rtag.tag_relation((sschema, rschema, oschema, role), displayinfo)
     displayinfo.setdefault('label', label)
 
-primaryview_display_ctrl = DisplayCtrlRelationTags(init_primaryview_display_ctrl)
+primaryview_display_ctrl = DisplayCtrlRelationTags('primaryview_display_ctrl',
+                                                   init_primaryview_display_ctrl)
 
 
 # index view configuration ####################################################
@@ -178,7 +182,7 @@
             section = 'generic'
         rtag.tag_relation((sschema, rschema, oschema, role), section)
 
-autoform_section = RelationTags(init_autoform_section,
+autoform_section = RelationTags('autoform_section', init_autoform_section,
                                 set(('primary', 'secondary', 'generic',
                                      'metadata', 'generated')))
 # use primary and not generated for eid since it has to be an hidden
@@ -217,7 +221,7 @@
 
 
 # relations'field class
-autoform_field = RelationTags()
+autoform_field = RelationTags('autoform_field')
 
 # relations'field explicit kwargs (given to field's __init__)
 autoform_field_kwargs = RelationTags()
@@ -231,7 +235,7 @@
 # inlined view flag for non final relations: when True for an entry, the
 # entity(ies) at the other end of the relation will be editable from the
 # form of the edited entity
-autoform_is_inlined = RelationTagsBool()
+autoform_is_inlined = RelationTagsBool('autoform_is_inlined')
 autoform_is_inlined.tag_subject_of(('*', 'use_email', '*'), True)
 autoform_is_inlined.tag_subject_of(('CWRelation', 'relation_type', '*'), True)
 autoform_is_inlined.tag_subject_of(('CWRelation', 'from_entity', '*'), True)
@@ -241,7 +245,7 @@
 # set of tags of the form <action>_on_new on relations. <action> is a
 # schema action (add/update/delete/read), and when such a tag is found
 # permissions checking is by-passed and supposed to be ok
-autoform_permissions_overrides = RelationTagsSet()
+autoform_permissions_overrides = RelationTagsSet('autoform_permissions_overrides')
 
 
 # boxes.EditBox configuration #################################################
@@ -254,7 +258,8 @@
                rschema.rproperty(sschema, oschema, 'composite') == role:
             rtag.tag_relation((sschema, rschema, oschema, role), True)
 
-actionbox_appearsin_addmenu = RelationTagsBool(init_actionbox_appearsin_addmenu)
+actionbox_appearsin_addmenu = RelationTagsBool('actionbox_appearsin_addmenu',
+                                               init_actionbox_appearsin_addmenu)
 actionbox_appearsin_addmenu.tag_subject_of(('*', 'is', '*'), False)
 actionbox_appearsin_addmenu.tag_object_of(('*', 'is', '*'), False)
 actionbox_appearsin_addmenu.tag_subject_of(('*', 'is_instance_of', '*'), False)
@@ -279,3 +284,4 @@
 actionbox_appearsin_addmenu.tag_object_of(('*', 'allowed_transition', 'Transition'), True)
 actionbox_appearsin_addmenu.tag_object_of(('*', 'destination_state', 'State'), True)
 actionbox_appearsin_addmenu.tag_subject_of(('State', 'allowed_transition', '*'), True)
+
--- a/web/views/__init__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/__init__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Views, forms, actions... for the CubicWeb web client
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/web/views/actions.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/actions.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,15 +1,16 @@
 """Set of HTML base actions
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
 from cubicweb.vregistry import objectify_selector
 from cubicweb.selectors import (EntitySelector,
     one_line_rset, two_lines_rset, one_etype_rset, relation_possible,
-    non_final_entity,
+    nonempty_rset, non_final_entity,
     authenticated_user, match_user_groups, match_search_state,
     has_permission, has_add_permission,
     )
@@ -74,7 +75,7 @@
     if accept match.
     """
     id = 'select'
-    __select__ = match_search_state('linksearch') & match_searched_etype()
+    __select__ = match_search_state('linksearch') & nonempty_rset() & match_searched_etype()
 
     title = _('select')
     category = 'mainactions'
--- a/web/views/ajaxedit.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/ajaxedit.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Set of views allowing edition of entities/relations using ajax
 
 :organization: Logilab
-:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/web/views/apacherewrite.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/apacherewrite.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 are much more limited for the moment)
 
 :organization: Logilab
-:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 __docformat__ = "restructuredtext en"
@@ -28,7 +29,7 @@
     def match(self, **kwargs):
         self._match = self.condition.match(kwargs[self.match_part])
         return not self._match is None
-    
+
     def action_rewrite(self, path):
         for rgx, replace in self.rules:
             if not rgx.match(path) is None:
@@ -45,7 +46,7 @@
     def action_stop(self, path):
         return path
 
-    
+
 class ApacheURLRewrite(Component):
     """inherit from this class with actual rules to activate apache style rewriting
 
@@ -69,26 +70,29 @@
         RewriteRule ^/(data/.*) http://localhost:8080/$1 [L,P]
         RewriteRule ^/(json.*) http://localhost:8080/$1 [L,P]
         RewriteRule ^/(.*) http://localhost:8080/m_%1/$1 [L,P]
-    
+
     could be written (considering that no "host rewritting" is necessary):
 
-      class MyAppRules(ApacheURLRewrite): 
+      class MyAppRules(ApacheURLRewrite):
         rules = [
           RewriteCond('logilab\.fr', match='host',
                       rules=[('/(.*)', r'http://www.logilab.fr/\1')],
                       action='redirect'),
           RewriteCond('(www)\.logilab\.fr', match='host', action='stop'),
           RewriteCond('/(data|json)/', match='path', action='stop'),
-          RewriteCond('(?P<cat>.*)\.logilab\.fr', match='host', 
+          RewriteCond('(?P<cat>.*)\.logilab\.fr', match='host',
                       rules=[('/(.*)', r'/m_%(cat)s/\1')]),
         ]
     """
     __abstract__ = True
     id = 'urlrewriter'
     rules = []
-        
-    def rewrite(self, host, path):
-        for cond in self.rules:
+
+    def get_rules(self, req):
+        return self.rules
+
+    def rewrite(self, host, path, req):
+        for cond in self.get_rules(req):
             if cond.match(host=host, path=path):
                 return cond.process(path)
         return path
--- a/web/views/authentication.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/authentication.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """user authentication component
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/web/views/autoform.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/autoform.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """The automatic entity form.
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 _ = unicode
@@ -11,13 +12,13 @@
 
 from cubicweb import typed_eid
 from cubicweb.web import stdmsgs, uicfg
-from cubicweb.web.form import FieldNotFound, EntityFieldsForm
+from cubicweb.web.form import FieldNotFound
 from cubicweb.web.formfields import guess_field
 from cubicweb.web.formwidgets import Button, SubmitButton
-from cubicweb.web.views.editforms import toggleable_relation_link, relation_id
+from cubicweb.web.views import forms, editforms
 
 
-class AutomaticEntityForm(EntityFieldsForm):
+class AutomaticEntityForm(forms.EntityFieldsForm):
     """base automatic form to edit any entity.
 
     Designed to be fully generated from schema but highly configurable through:
@@ -32,7 +33,7 @@
     cwtarget = 'eformframe'
     cssclass = 'entityForm'
     copy_nav_params = True
-    form_buttons = [SubmitButton(stdmsgs.BUTTON_OK),
+    form_buttons = [SubmitButton(),
                     Button(stdmsgs.BUTTON_APPLY, cwaction='apply'),
                     Button(stdmsgs.BUTTON_CANCEL, cwaction='cancel')]
     attrcategories = ('primary', 'secondary')
@@ -189,7 +190,8 @@
         """
         # we'll need an initialized varmaker if there are some inlined relation
         self.initialize_varmaker()
-        return self.erelations_by_category(self.edited_entity, True, 'add', self.rinlined)
+        return self.erelations_by_category(self.edited_entity, True, 'add',
+                                           self.rinlined)
 
     def srelations_by_category(self, categories=None, permission=None):
         """filter out result of relations_by_category(categories, permission) by
@@ -233,13 +235,13 @@
         for label, rschema, role in self.srelations_by_category('generic', 'add'):
             relatedrset = entity.related(rschema, role, limit=self.related_limit)
             if rschema.has_perm(self.req, 'delete'):
-                toggleable_rel_link_func = toggleable_relation_link
+                toggleable_rel_link_func = editforms.toggleable_relation_link
             else:
                 toggleable_rel_link_func = lambda x, y, z: u''
             related = []
             for row in xrange(relatedrset.rowcount):
-                nodeid = relation_id(entity.eid, rschema, role,
-                                     relatedrset[row][0])
+                nodeid = editforms.relation_id(entity.eid, rschema, role,
+                                               relatedrset[row][0])
                 if nodeid in pending_deletes:
                     status = u'pendingDelete'
                     label = '+'
--- a/web/views/basecomponents.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/basecomponents.py	Wed Jun 03 09:15:20 2009 +0200
@@ -4,10 +4,12 @@
 * the logged user link
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
+_ = unicode
 
 from rql import parse
 
@@ -18,10 +20,8 @@
 from cubicweb.web.htmlwidgets import (MenuWidget, PopupBoxMenu, BoxSeparator,
                                       BoxLink)
 
-_ = unicode
-
 VISIBLE_PROP_DEF = {
-    _('visible'):  dict(type='Boolean', default=False,
+    _('visible'):  dict(type='Boolean', default=True,
                         help=_('display the component or not')),
     }
 
@@ -126,9 +126,8 @@
     """
     __select__ = yes()
     id = 'applmessages'
-    property_defs = VISIBLE_PROP_DEF
     # don't want user to hide this component using an cwproperty
-    site_wide = True
+    property_defs = {}
 
     def call(self):
         msgs = [msg for msg in (self.req.get_shared_data('sources_error', pop=True),
--- a/web/views/basecontrollers.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/basecontrollers.py	Wed Jun 03 09:15:20 2009 +0200
@@ -4,8 +4,9 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -14,16 +15,17 @@
 import simplejson
 
 from logilab.common.decorators import cached
+from logilab.mtconverter import html_escape
 
 from cubicweb import NoSelectableObject, ValidationError, ObjectNotFound, typed_eid
 from cubicweb.utils import strptime
 from cubicweb.selectors import yes, match_user_groups
-from cubicweb.view import STRICT_DOCTYPE
+from cubicweb.view import STRICT_DOCTYPE, STRICT_DOCTYPE_NOEXT
 from cubicweb.common.mail import format_mail
 from cubicweb.web import ExplicitLogin, Redirect, RemoteCallFailed, json_dumps
-from cubicweb.web.formrenderers import FormRenderer
 from cubicweb.web.controller import Controller
 from cubicweb.web.views import vid_from_rset
+from cubicweb.web.views.formrenderers import FormRenderer
 try:
     from cubicweb.web.facet import (FilterRQLBuilder, get_facet,
                                     prepare_facets_rqlst)
@@ -32,8 +34,13 @@
     HAS_SEARCH_RESTRICTION = False
 
 
-def xhtml_wrap(source):
-    head = u'<?xml version="1.0"?>\n' + STRICT_DOCTYPE
+def xhtml_wrap(self, source):
+    # XXX factor out, watch view.py ~ Maintemplate.doctype
+    if self.req.xhtml_browser():
+        dt = STRICT_DOCTYPE
+    else:
+        dt = STRICT_DOCTYPE_NOEXT
+    head = u'<?xml version="1.0"?>\n' + dt
     return head + u'<div xmlns="http://www.w3.org/1999/xhtml" xmlns:cubicweb="http://www.logilab.org/2008/cubicweb">%s</div>' % source.strip()
 
 def jsonize(func):
@@ -51,7 +58,7 @@
     def wrapper(self, *args, **kwargs):
         self.req.set_content_type(self.req.html_content_type())
         result = func(self, *args, **kwargs)
-        return xhtml_wrap(result)
+        return xhtml_wrap(self, result)
     wrapper.__name__ = func.__name__
     return wrapper
 
@@ -114,6 +121,8 @@
             try:
                 method = getattr(entity, req.form.pop('__method'))
                 method()
+            except Redirect: # propagate redirect that might occur in method()
+                raise
             except Exception, ex:
                 self.exception('while handling __method')
                 req.set_message(req._("error while handling __method: %s") % req._(ex))
@@ -203,7 +212,7 @@
         self.req.set_content_type('text/html')
         jsarg = simplejson.dumps( (status, args) )
         return """<script type="text/javascript">
- window.parent.handleFormValidationResponse('entityForm', null, %s);
+ window.parent.handleFormValidationResponse('entityForm', null, null, %s);
 </script>""" %  simplejson.dumps( (status, args) )
 
     def validation_error(self, err):
@@ -332,7 +341,7 @@
                                        entity=entity)
         form.form_build_context()
         vfield = form.field_by_name('value')
-        renderer = FormRenderer()
+        renderer = FormRenderer(self.req)
         return vfield.render(form, renderer, tabindex=tabindex) \
                + renderer.render_help(form, vfield)
 
@@ -377,11 +386,7 @@
             ctrl.publish(None, fromjson=True)
         except ValidationError, err:
             self.req.cnx.rollback()
-            if not err.entity or isinstance(err.entity, (long, int)):
-                eid = err.entity
-            else:
-                eid = err.entity.eid
-            return (False, (eid, err.errors))
+            return (False, (err.entity, err.errors))
         except Redirect, redir:
             return (True, redir.location)
         except Exception, err:
@@ -413,6 +418,8 @@
             rset = entity.related(rtype, role)
             if rset:
                 output = self.view(vid, rset)
+                if vid == 'textoutofcontext':
+                    output = html_escape(output)
             else:
                 output = default
             return (success, args, output)
@@ -482,6 +489,24 @@
         rql = 'DELETE B bookmarked_by U WHERE B eid %(b)s, U eid %(u)s'
         self.req.execute(rql, {'b': typed_eid(beid), 'u' : self.req.user.eid})
 
+    def js_node_clicked(self, treeid, nodeeid):
+        """add/remove eid in treestate cookie"""
+        from cubicweb.web.views.treeview import treecookiename
+        cookies = self.req.get_cookie()
+        statename = treecookiename(treeid)
+        treestate = cookies.get(statename)
+        if treestate is None:
+            cookies[statename] = nodeeid
+            self.req.set_cookie(cookies, statename)
+        else:
+            marked = set(filter(None, treestate.value.split(';')))
+            if nodeeid in marked:
+                marked.remove(nodeeid)
+            else:
+                marked.add(nodeeid)
+            cookies[statename] = ';'.join(marked)
+            self.req.set_cookie(cookies, statename)
+
     def js_set_cookie(self, cookiename, cookievalue):
         # XXX we should consider jQuery.Cookie
         cookiename, cookievalue = str(cookiename), str(cookievalue)
--- a/web/views/baseforms.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/baseforms.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 or a list of entities of the same type
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -24,8 +25,8 @@
 from cubicweb.web.views.autoform import AutomaticEntityForm
 
 _ = unicode
-    
-    
+
+
 class EditionForm(FormMixIn, EntityView):
     """primary entity edition form
 
@@ -34,14 +35,14 @@
     name of the attribute being edited. You may use this feature to compute
     dynamic default values such as the 'tomorrow' date or the user's login
     being connected
-    """    
+    """
     id = 'edition'
     __select__ = one_line_rset() & non_final_entity()
 
     title = _('edition')
     controller = 'edit'
     skip_relations = set()
-    
+
     EDITION_BODY = u'''\
  %(errormsg)s
 <form id="%(formid)s" class="entityForm" cubicweb:target="eformframe"
@@ -135,7 +136,7 @@
     @property
     def formid(self):
         return self.id
-    
+
     def action_title(self, entity):
         """form's title"""
         ptitle = self.req._(self.title)
@@ -155,7 +156,7 @@
                 output.append(u'<input type="hidden" name="%s" value="%s" />'
                               % (name, value))
         return u'\n'.join(output)
-                
+
     def add_hidden_web_behaviour_params(self, entity):
         """inserts hidden params controlling how errors and redirection
         should be handled
@@ -174,8 +175,8 @@
             self._hiddens.append( ('__linkto', linkto, '') )
             msg = '%s %s' % (msg, self.req._('and linked'))
         self._hiddens.append( ('__message', msg, '') )
-        
-    
+
+
     def attributes_form(self, entity, kwargs, include_eid=True):
         """create a form to edit entity's attributes"""
         html = []
@@ -208,7 +209,7 @@
         # XXX both (add, delete)
         return [(rschema, x) for rschema, _, x in entity.relations_by_category(('primary', 'secondary'), 'add')
                 if rschema != 'eid']
-    
+
     def relations_form(self, entity, kwargs):
         srels_by_cat = entity.srelations_by_category(('generic', 'metadata'), 'add')
         if not srels_by_cat:
@@ -267,7 +268,7 @@
         w(u'</table>')
         w(u'</fieldset>')
         return '\n'.join(html)
-        
+
     def inline_entities_form(self, entity, kwargs):
         """create a form to edit entity's inlined relations"""
         result = []
@@ -286,7 +287,7 @@
                 existant = entity.has_eid() and entity.related(rschema)
                 if existant:
                     # display inline-edition view for all existing related entities
-                    result.append(self.view('inline-edition', existant, 
+                    result.append(self.view('inline-edition', existant,
                                             ptype=entity.e_schema, peid=entity.eid,
                                             rtype=rschema, role=x, **kwargs))
                 if x == 'subject':
@@ -318,7 +319,7 @@
         return '\n'.join(result)
 
     # should_* method extracted to allow overriding
-    
+
     def should_inline_relation_form(self, entity, rschema, targettype, role):
         return AutomaticEntityForm.rinlined.etype_get(entity.id, rschema, role,
                                                       targettype)
@@ -328,10 +329,10 @@
 
     def should_display_add_inline_relation_link(self, rschema, existant, card):
         return not existant or card in '+*'
-    
+
     def reset_url(self, entity):
         return entity.absolute_url()
-    
+
     def on_submit(self, entity):
         return u'return freezeFormButtons(\'%s\')' % (self.domid)
 
@@ -339,7 +340,7 @@
         return self.req._('element edited')
 
 
-    
+
 class CreationForm(EditionForm):
     __select__ = specified_etype_implements('Any')
     # XXX bw compat, use View.registered since we don't want accept_compat
@@ -347,7 +348,7 @@
     registered = accepts_etype_compat(View.registered)
     id = 'creation'
     title = _('creation')
-    
+
     def call(self, **kwargs):
         """creation view for an entity"""
         self.req.add_js( ('cubicweb.ajax.js',) )
@@ -385,16 +386,16 @@
     @property
     def formid(self):
         return 'edition'
-    
+
     def relations_form(self, entity, kwargs):
         return u''
 
     def reset_url(self, entity=None):
         return self.build_url(self.req.form.get('etype', '').lower())
-    
+
     def submited_message(self):
         return self.req._('element created')
-    
+
     def url(self):
         """return the url associated with this view"""
         return self.create_url(self.req.form.get('etype'))
@@ -405,21 +406,21 @@
     @cached
     def card(self, etype):
         return self.rschema.rproperty(self.parent_schema, etype, 'cardinality')[0]
-    
+
     def action_title(self, entity):
         return self.rschema.display_name(self.req, self.role)
-        
+
     def add_hidden_web_behaviour_params(self, entity):
         pass
-    
+
     def edit_form(self, entity, ptype, peid, rtype,
                   role='subject', **kwargs):
         self.rschema = self.schema.rschema(rtype)
-        self.role = role        
+        self.role = role
         self.parent_schema = self.schema.eschema(ptype)
         self.parent_eid = peid
         super(InlineFormMixIn, self).edit_form(entity, kwargs)
-    
+
     def should_inline_relation_form(self, entity, rschema, targettype, role):
         if rschema == self.rschema:
             return False
@@ -455,7 +456,7 @@
                      }
         ctx.update(local_ctx)
         return ctx
-    
+
 
 class CopyEditionForm(EditionForm):
     id = 'copy'
@@ -465,7 +466,7 @@
         self.req.add_js(('cubicweb.ajax.js',))
         entity = self.complete_entity(row, col, skip_bytes=True)
         # make a copy of entity to avoid altering the entity in the
-        # request's cache. 
+        # request's cache.
         self.newentity = copy(entity)
         self.copying = self.newentity.eid
         self.newentity.eid = None
@@ -482,23 +483,23 @@
     @property
     def formid(self):
         return 'edition'
-        
+
     def relations_form(self, entity, kwargs):
         return u''
 
     def reset_url(self, entity):
         return self.build_url('view', rql='Any X WHERE X eid %s' % self.copying)
-    
+
     def attributes_form(self, entity, kwargs, include_eid=True):
         # we don't want __clone_eid on inlined edited entities
         if entity.eid == self.newentity.eid:
             self._hiddens.append((eid_param('__cloned_eid', entity.eid), self.copying, ''))
         return EditionForm.attributes_form(self, entity, kwargs, include_eid)
-    
+
     def submited_message(self):
         return self.req._('element copied')
-       
-    
+
+
 class TableEditForm(FormMixIn, EntityView):
     id = 'muledit'
     title = _('multiple edit')
@@ -526,7 +527,7 @@
       </td>
     </tr>
   </table>
-  </fieldset>    
+  </fieldset>
 </form>
 '''
 
@@ -535,7 +536,7 @@
   %(error)s
   <div>%(widget)s</div>
 </td>'''
-    
+
     def call(self, **kwargs):
         """a view to edit multiple entities of the same type
         the first column should be the eid
@@ -560,13 +561,13 @@
                'oktitle': _('validate modifications on selected items').capitalize(),
                'cancelvalue': _('button_reset').capitalize(),
                'canceltitle': _('revert changes').capitalize(),
-               }        
+               }
         self.w(self.EDITION_BODY % ctx)
-        
-        
+
+
     def reset_url(self, entity=None):
         self.build_url('view', rql=self.rset.printable_rql())
-        
+
     def edit_form(self, entity):
         html = []
         w = html.append
--- a/web/views/basetemplates.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/basetemplates.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,12 +2,12 @@
 """default templates for CubicWeb web client
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
-
 from logilab.mtconverter import html_escape
 
 from cubicweb.vregistry import objectify_selector
--- a/web/views/baseviews.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/baseviews.py	Wed Jun 03 09:15:20 2009 +0200
@@ -7,12 +7,13 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 #from __future__ import with_statement
-
 __docformat__ = "restructuredtext en"
+_ = unicode
 
 from rql import nodes
 
@@ -23,7 +24,6 @@
 from cubicweb.view import EntityView, AnyRsetView, View
 from cubicweb.common.uilib import cut, printable_value
 
-_ = unicode
 
 class NullView(AnyRsetView):
     """default view when no result has been found"""
@@ -204,9 +204,10 @@
     def cell_call(self, row, col):
         entity = self.entity(row, col)
         desc = cut(entity.dc_description(), 50)
-        self.w(u'<a href="%s" title="%s">' % (html_escape(entity.absolute_url()),
-                                              html_escape(desc)))
-        self.w(html_escape(self.view('textincontext', self.rset, row=row, col=col)))
+        self.w(u'<a href="%s" title="%s">' % (
+            html_escape(entity.absolute_url()), html_escape(desc)))
+        self.w(html_escape(self.view('textincontext', self.rset,
+                                     row=row, col=col)))
         self.w(u'</a>')
 
 
@@ -214,8 +215,12 @@
     id = 'outofcontext'
 
     def cell_call(self, row, col):
-        self.w(u'<a href="%s">' % self.entity(row, col).absolute_url())
-        self.w(html_escape(self.view('textoutofcontext', self.rset, row=row, col=col)))
+        entity = self.entity(row, col)
+        desc = cut(entity.dc_description(), 50)
+        self.w(u'<a href="%s" title="%s">' % (
+            html_escape(entity.absolute_url()), html_escape(desc)))
+        self.w(html_escape(self.view('textoutofcontext', self.rset,
+                                     row=row, col=col)))
         self.w(u'</a>')
 
 
--- a/web/views/bookmark.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/bookmark.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Primary view for bookmarks + user's bookmarks box
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/web/views/boxes.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/boxes.py	Wed Jun 03 09:15:20 2009 +0200
@@ -9,10 +9,12 @@
 * startup views box
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
+_ = unicode
 
 from logilab.mtconverter import html_escape
 
@@ -23,7 +25,6 @@
 from cubicweb.web import uicfg
 from cubicweb.web.box import BoxTemplate
 
-_ = unicode
 
 class EditBox(BoxTemplate):
     """
@@ -99,10 +100,10 @@
         eschema = entity.e_schema
         for rschema, teschema, x in self.add_related_schemas(entity):
             if x == 'subject':
-                label = '%s %s %s %s' % (eschema, rschema, teschema, x)
+                label = 'add %s %s %s %s' % (eschema, rschema, teschema, x)
                 url = self.linkto_url(entity, rschema, teschema, 'object')
             else:
-                label = '%s %s %s %s' % (teschema, rschema, eschema, x)
+                label = 'add %s %s %s %s' % (teschema, rschema, eschema, x)
                 url = self.linkto_url(entity, rschema, teschema, 'subject')
             actions.append(self.mk_action(_(label), url))
         return actions
--- a/web/views/calendar.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/calendar.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,9 +1,12 @@
 """html calendar views
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
+__docformat__ = "restructuredtext en"
+_ = unicode
 
 from datetime import datetime, date, timedelta
 
@@ -13,9 +16,7 @@
 from cubicweb.selectors import implements
 from cubicweb.utils import strptime, date_range, todate, todatetime
 from cubicweb.view import EntityView
-from cubicweb.web import ajax_replace_url
 
-_ = unicode
 
 # useful constants & functions ################################################
 
@@ -82,7 +83,8 @@
             task = self.complete_entity(i)
             self.w(u'<div class="vevent">')
             self.w(u'<h3 class="summary">%s</h3>' % html_escape(task.dc_title()))
-            self.w(u'<div class="description">%s</div>' % html_escape(task.dc_description()))
+            self.w(u'<div class="description">%s</div>'
+                   % task.dc_description(format='text/html'))
             if task.start:
                 self.w(u'<abbr class="dtstart" title="%s">%s</abbr>' % (task.start.isoformat(), self.format_date(task.start)))
             if task.stop:
@@ -164,13 +166,15 @@
             else:
                 user = None
             the_dates = []
-            tstart = todate(task.start)
+            tstart = task.start
             if tstart:
+                tstart = todate(task.start)
                 if tstart > lastday:
                     continue
                 the_dates = [tstart]
-            tstop = todate(task.start)
+            tstop = task.stop
             if tstop:
+                tstop = todate(tstop)
                 if tstop < firstday:
                     continue
                 the_dates = [tstop]
@@ -260,10 +264,12 @@
         prevdate = curdate - timedelta(31)
         nextdate = curdate + timedelta(31)
         rql = self.rset.printable_rql()
-        prevlink = ajax_replace_url('onemonthcalid', rql, 'onemonthcal',
-                                    year=prevdate.year, month=prevdate.month)
-        nextlink = ajax_replace_url('onemonthcalid', rql, 'onemonthcal',
-                                    year=nextdate.year, month=nextdate.month)
+        prevlink = self.req.build_ajax_replace_url('onemonthcalid', rql, 'onemonthcal',
+                                                   year=prevdate.year,
+                                                   month=prevdate.month)
+        nextlink = self.req.build_ajax_replace_url('onemonthcalid', rql, 'onemonthcal',
+                                                   year=nextdate.year,
+                                                   month=nextdate.month)
         return prevlink, nextlink
 
     def _build_calendar_cell(self, celldate, rows, curdate):
@@ -348,13 +354,15 @@
                 continue
             done_tasks.append(task)
             the_dates = []
-            tstart = todate(task.start)
-            tstop = todate(task.stop)
+            tstart = task.start
+            tstop = task.stop
             if tstart:
+                tstart = todate(tstart)
                 if tstart > lastday:
                     continue
                 the_dates = [tstart]
             if tstop:
+                tstop = todate(tstop)
                 if tstop < firstday:
                     continue
                 the_dates = [tstop]
@@ -513,9 +521,10 @@
         prevdate = curdate - timedelta(7)
         nextdate = curdate + timedelta(7)
         rql = self.rset.printable_rql()
-        prevlink = ajax_replace_url('oneweekcalid', rql, 'oneweekcal',
-                                    year=prevdate.year, week=prevdate.isocalendar()[1])
-        nextlink = ajax_replace_url('oneweekcalid', rql, 'oneweekcal',
-                                    year=nextdate.year, week=nextdate.isocalendar()[1])
+        prevlink = self.req.build_ajax_replace_url('oneweekcalid', rql, 'oneweekcal',
+                                                   year=prevdate.year,
+                                                   week=prevdate.isocalendar()[1])
+        nextlink = self.req.build_ajax_replace_url('oneweekcalid', rql, 'oneweekcal',
+                                                   year=nextdate.year,
+                                                   week=nextdate.isocalendar()[1])
         return prevlink, nextlink
-
--- a/web/views/csvexport.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/csvexport.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """csv export views
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/web/views/cwproperties.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/cwproperties.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Specific views for CWProperty
 
 :organization: Logilab
-:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 _ = unicode
@@ -16,13 +17,13 @@
                                 match_user_groups)
 from cubicweb.view import StartupView
 from cubicweb.web import uicfg, stdmsgs
-from cubicweb.web.form import CompositeForm, EntityFieldsForm, FormViewMixIn
+from cubicweb.web.form import FormViewMixIn
 from cubicweb.web.formfields import FIELDS, StringField
 from cubicweb.web.formwidgets import Select, Button, SubmitButton
-from cubicweb.web.views import primary
+from cubicweb.web.views import primary, formrenderers
 
 
-# some string we want to be internationalizable for nicer display of eproperty
+# some string we want to be internationalizable for nicer display of property
 # groups
 _('navigation')
 _('ui')
@@ -48,10 +49,9 @@
 _('category')
 
 
-def make_togglable_link(nodeid, label, cookiename):
+def make_togglable_link(nodeid, label):
     """builds a HTML link that switches the visibility & remembers it"""
-    action = u"javascript: toggleVisibility('%s', '%s')" % \
-        (nodeid, cookiename)
+    action = u"javascript: togglePrefVisibility('%s')" % nodeid
     return u'<a href="%s">%s</a>' % (action, label)
 
 def css_class(someclass):
@@ -63,8 +63,8 @@
     skip_none = False
 
 
-class SystemEPropertiesForm(FormViewMixIn, StartupView):
-    id = 'systemepropertiesform'
+class SystemCWPropertiesForm(FormViewMixIn, StartupView):
+    id = 'systempropertiesform'
     __select__ = none_rset() & match_user_groups('managers')
 
     title = _('site configuration')
@@ -95,15 +95,15 @@
 
     def call(self, **kwargs):
         """The default view representing the application's index"""
-        self.req.add_js('cubicweb.preferences.js')
+        self.req.add_js(('cubicweb.edition.js', 'cubicweb.preferences.js', 'cubicweb.ajax.js'))
         self.req.add_css('cubicweb.preferences.css')
         vreg = self.vreg
         values = self.defined_keys
         groupedopts = {}
         mainopts = {}
-        # "self.id=='systemepropertiesform'" to skip site wide properties on
+        # "self.id=='systempropertiesform'" to skip site wide properties on
         # user's preference but not site's configuration
-        for key in vreg.user_property_keys(self.id=='systemepropertiesform'):
+        for key in vreg.user_property_keys(self.id=='systempropertiesform'):
             parts = key.split('.')
             if parts[0] in vreg:
                 # appobject configuration
@@ -113,61 +113,73 @@
                 mainopts.setdefault(parts[0], []).append(key)
         # precompute form to consume error message
         for group, keys in mainopts.items():
-            mainopts[group] = self.form(keys, True)
+            mainopts[group] = self.form(group, keys, False)
+
         for group, objects in groupedopts.items():
             for oid, keys in objects.items():
-                groupedopts[group][oid] = self.form(keys, True)
+                groupedopts[group][oid] = self.form(group + '-' + oid, keys, True)
+
         w = self.w
         req = self.req
         _ = req._
         w(u'<h1>%s</h1>\n' % _(self.title))
-        # we don't want this in each sub-forms
-        w(u'<div id="progress">%s</div>' % self.req._('validating...'))
         for label, group, form in sorted((_(g), g, f)
                                          for g, f in mainopts.iteritems()):
             status = css_class(self._group_status(group))
             w(u'<h2 class="propertiesform">%s</h2>\n' %
-              (make_togglable_link('fieldset_' + group, label,
-                                   self._cookie_name(group))))
+            (make_togglable_link('fieldset_' + group, label.capitalize())))
             w(u'<div id="fieldset_%s" %s>' % (group, status))
+            w(u'<fieldset class="preferences">')
             w(form)
-            w(u'</div>')
+            w(u'</fieldset></div>')
+
         for label, group, objects in sorted((_(g), g, o)
                                             for g, o in groupedopts.iteritems()):
             status = css_class(self._group_status(group))
             w(u'<h2 class="propertiesform">%s</h2>\n' %
-              (make_togglable_link('fieldset_' + group, label,
-                                   self._cookie_name(group))))
+              (make_togglable_link('fieldset_' + group, label.capitalize())))
             w(u'<div id="fieldset_%s" %s>' % (group, status))
-            for label, oid, form in sorted((self.req.__('%s_%s' % (group, o)), o, f)
-                                           for o, f in objects.iteritems()):
-                w(u'<fieldset class="subentity">')
-                w(u'<legend class="componentTitle">%s</legend>\n' % label)
+
+            # create selection
+            sorted_objects =  sorted((self.req.__('%s_%s' % (group, o)), o, f)
+                                           for o, f in objects.iteritems())
+            for label, oid, form in sorted_objects:
+                w(u'<div class="component">')
+                w(u'''<div class="componentLink"><a href="javascript:noop();"
+                           onclick="javascript:toggleVisibility('field_%(oid)s_%(group)s')"
+                           class="componentTitle">%(label)s</a>''' % {'label':label, 'oid':oid, 'group':group})
+                w(u''' (<div class="openlink"><a href="javascript:noop();"
+                             onclick="javascript:openFieldset('fieldset_%(group)s')">%(label)s</a></div>)'''
+                  % {'label':_('open all'), 'group':group})
+                w(u'</div>')
                 docmsgid = '%s_%s_description' % (group, oid)
                 doc = _(docmsgid)
                 if doc != docmsgid:
-                    w(u'<p class="description">%s</p>' % html_escape(doc))
+                    w(u'<div class="helper">%s</div>' % html_escape(doc).capitalize())
+                w(u'</div>')
+                w(u'<fieldset id="field_%(oid)s_%(group)s" class="%(group)s preferences hidden">'
+                  % {'oid':oid, 'group':group})
                 w(form)
                 w(u'</fieldset>')
             w(u'</div>')
 
     @property
     @cached
-    def eprops_rset(self):
+    def cwprops_rset(self):
         return self.req.execute('Any P,K,V WHERE P is CWProperty, P pkey K, '
                                 'P value V, NOT P for_user U')
 
     @property
     def defined_keys(self):
         values = {}
-        for i, entity in enumerate(self.eprops_rset.entities()):
+        for i, entity in enumerate(self.cwprops_rset.entities()):
             values[entity.pkey] = i
         return values
 
     def entity_for_key(self, key):
         values = self.defined_keys
         if key in values:
-            entity = self.eprops_rset.get_entity(values[key], 0)
+            entity = self.cwprops_rset.get_entity(values[key], 0)
         else:
             entity = self.vreg.etype_class('CWProperty')(self.req, None, None)
             entity.eid = self.req.varmaker.next()
@@ -175,12 +187,13 @@
             entity['value'] = self.vreg.property_value(key)
         return entity
 
-    def form(self, keys, splitlabel=False):
-        buttons = [SubmitButton(),
-                   Button(stdmsgs.BUTTON_CANCEL, cwaction='cancel')]
-        form = CompositeForm(self.req, domid=None, action=self.build_url(),
-                             form_buttons=buttons,
-                             submitmsg=self.req._('changes applied'))
+    def form(self, formid, keys, splitlabel=False):
+        buttons = [SubmitButton()]
+        form = self.vreg.select_object('forms', 'composite', self.req,
+                                  domid=formid, action=self.build_url(),
+                                  form_buttons=buttons,
+                                  onsubmit="return validatePrefsForm('%s')" % formid,
+                                  submitmsg=self.req._('changes applied'))
         path = self.req.relative_path()
         if '?' in path:
             path, params = path.split('?', 1)
@@ -188,7 +201,9 @@
         form.form_add_hidden('__redirectpath', path)
         for key in keys:
             self.form_row(form, key, splitlabel)
-        return form.form_render(display_progress_div=False)
+        renderer = self.vreg.select_object('formrenderers', 'cwproperties', self.req,
+                                           display_progress_div=False)
+        return form.form_render(renderer=renderer)
 
     def form_row(self, form, key, splitlabel):
         entity = self.entity_for_key(key)
@@ -196,21 +211,22 @@
             label = key.split('.')[-1]
         else:
             label = key
-        subform = EntityFieldsForm(self.req, entity=entity, set_error_url=False)
+        subform = self.vreg.select_object('forms', 'base', self.req, entity=entity,
+                                     set_error_url=False)
         subform.append_field(PropertyValueField(name='value', label=label,
                                                 eidparam=True))
         subform.vreg = self.vreg
         subform.form_add_hidden('pkey', key, eidparam=True)
+        subform.form_add_hidden("current-value:%s" % entity.eid,)
         form.form_add_subform(subform)
         return subform
 
-
 def is_user_prefs(cls, req, rset, row=None, col=0, **kwargs):
     return req.user.eid == rset[row or 0][col]
 
 
-class EPropertiesForm(SystemEPropertiesForm):
-    id = 'epropertiesform'
+class CWPropertiesForm(SystemCWPropertiesForm):
+    id = 'propertiesform'
     __select__ = (
         # we don't want guests to be able to come here
         match_user_groups('users', 'managers') &
@@ -228,19 +244,19 @@
 
     @property
     @cached
-    def eprops_rset(self):
+    def cwprops_rset(self):
         return self.req.execute('Any P,K,V WHERE P is CWProperty, P pkey K, P value V,'
                                 'P for_user U, U eid %(x)s', {'x': self.user.eid})
 
     def form_row(self, form, key, splitlabel):
-        subform = super(EPropertiesForm, self).form_row(form, key, splitlabel)
+        subform = super(CWPropertiesForm, self).form_row(form, key, splitlabel)
         # if user is in the managers group and the property is being created,
         # we have to set for_user explicitly
         if not subform.edited_entity.has_eid() and self.user.matching_groups('managers'):
             subform.form_add_hidden('for_user', self.user.eid, eidparam=True)
 
 
-# eproperty form objects ######################################################
+# cwproperty form objects ######################################################
 
 class PlaceHolderWidget(object):
 
@@ -341,3 +357,30 @@
 
 uicfg.autoform_field.tag_attribute(('CWProperty', 'pkey'), PropertyKeyField)
 uicfg.autoform_field.tag_attribute(('CWProperty', 'value'), PropertyValueField)
+
+
+class CWPropertiesFormRenderer(formrenderers.FormRenderer):
+    """specific renderer for properties"""
+    id = 'cwproperties'
+
+    def open_form(self, form, values):
+        err = '<div class="formsg"></div>'
+        return super(CWPropertiesFormRenderer, self).open_form(form, values) + err
+
+    def _render_fields(self, fields, w, form):
+        for field in fields:
+            w(u'<div class="preffield">\n')
+            if self.display_label:
+                w(u'%s' % self.render_label(form, field))
+            error = form.form_field_error(field)
+            w(u'%s' % self.render_help(form, field))
+            w(u'<div class="prefinput">')
+            w(field.render(form, self))
+            w(u'</div>')
+            w(u'</div>')
+
+    def render_buttons(self, w, form):
+        w(u'<div>\n')
+        for button in form.form_buttons:
+            w(u'%s\n' % button.render(form))
+        w(u'</div>')
--- a/web/views/cwuser.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/cwuser.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Specific views for users
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/web/views/debug.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/debug.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -23,7 +24,7 @@
                 html_escape(str(key)), html_escape(repr(dict[key]))))
         w(u'</ul>')
 
-    
+
 class DebugView(StartupView):
     id = 'debug'
     __select__ = none_rset() & match_user_groups('managers')
--- a/web/views/editcontroller.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/editcontroller.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,10 +1,12 @@
 """The edit controller, handling form submitting.
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
+
 from decimal import Decimal
 
 from rql.utils import rqlvar_maker
@@ -228,6 +230,9 @@
                         formparams['name'] = value[0]
                         self.relations.append('X name %(name)s')
                     value = val
+            else:
+                # no specified value, skip
+                return
         elif value is not None:
             if attrtype in ('Date', 'Datetime', 'Time'):
                 try:
--- a/web/views/editforms.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/editforms.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,10 +2,12 @@
 or a list of entities of the same type
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
+_ = unicode
 
 from copy import copy
 
@@ -19,14 +21,11 @@
 from cubicweb.view import EntityView
 from cubicweb.common import tags
 from cubicweb.web import INTERNAL_FIELD_VALUE, stdmsgs, eid_param
-from cubicweb.web.form import CompositeForm, EntityFieldsForm, FormViewMixIn
+from cubicweb.web.form import FormViewMixIn
 from cubicweb.web.formfields import RelationField
 from cubicweb.web.formwidgets import Button, SubmitButton, ResetButton, Select
-from cubicweb.web.formrenderers import (FormRenderer, EntityFormRenderer,
-                                        EntityCompositeFormRenderer,
-                                        EntityInlinedFormRenderer)
+from cubicweb.web.views import forms
 
-_ = unicode
 
 def relation_id(eid, rtype, role, reid):
     """return an identifier for a relation between two entities"""
@@ -52,7 +51,7 @@
     # else we will only delete the displayed page
     need_navigation = False
 
-    def call(self):
+    def call(self, onsubmit=None):
         """ask for confirmation before real deletion"""
         req, w = self.req, self.w
         _ = req._
@@ -60,17 +59,19 @@
           % _('this action is not reversible!'))
         # XXX above message should have style of a warning
         w(u'<h4>%s</h4>\n' % _('Do you want to delete the following element(s) ?'))
-        form = CompositeForm(req, domid='deleteconf', copy_nav_params=True,
-                             action=self.build_url('edit'), onsubmit=None,
-                             form_buttons=[Button(stdmsgs.YES, cwaction='delete'),
-                                           Button(stdmsgs.NO, cwaction='cancel')])
+        form = self.vreg.select_object('forms', 'composite', req, domid='deleteconf',
+                                       copy_nav_params=True,
+                                       action=self.build_url('edit'), onsubmit=onsubmit,
+                                       form_buttons=[Button(stdmsgs.YES, cwaction='delete'),
+                                                     Button(stdmsgs.NO, cwaction='cancel')])
         done = set()
         w(u'<ul>\n')
         for entity in self.rset.entities():
             if entity.eid in done:
                 continue
             done.add(entity.eid)
-            subform = EntityFieldsForm(req, entity=entity, set_error_url=False)
+            subform = self.vreg.select_object('forms', 'base', req, entity=entity,
+                                              set_error_url=False)
             form.form_add_subform(subform)
             # don't use outofcontext view or any other that may contain inline edition form
             w(u'<li>%s</li>' % tags.a(entity.view('textoutofcontext'),
@@ -113,14 +114,18 @@
             self.w(value)
             return
         if rschema.is_final():
-            form = self._build_attribute_form(entity, value, rtype, role, reload, row, col, default)
+            form = self._build_attribute_form(entity, value, rtype, role,
+                                              reload, row, col, default)
         else:
-            form = self._build_relation_form(entity, value, rtype, role, row, col, vid, default)
+            form = self._build_relation_form(entity, value, rtype, role,
+                                             row, col, vid, default)
         form.form_add_hidden(u'__maineid', entity.eid)
-        renderer = FormRenderer(display_label=False, display_help=False,
-                                display_fields=[(rtype, role)],
-                                table_class='', button_bar_class='buttonbar',
-                                display_progress_div=False)
+        renderer = self.vreg.select_object('formrenderers', 'base', self.req,
+                                      entity=entity,
+                                      display_label=False, display_help=False,
+                                      display_fields=[(rtype, role)],
+                                      table_class='', button_bar_class='buttonbar',
+                                      display_progress_div=False)
         self.w(form.form_render(renderer=renderer))
 
     def _build_relation_form(self, entity, value, rtype, role, row, col, vid, default):
@@ -128,16 +133,17 @@
         divid = 'd%s' % make_uid('%s-%s' % (rtype, entity.eid))
         event_data = {'divid' : divid, 'eid' : entity.eid, 'rtype' : rtype, 'vid' : vid,
                       'default' : default, 'role' : role}
-        form = EntityFieldsForm(self.req, None, entity=entity, action='#',
-                                domid='%s-form' % divid,
-                                cssstyle='display: none',
-                                onsubmit=("return inlineValidateRelationForm('%(divid)s-form', '%(rtype)s', "
-                                          "'%(role)s', '%(eid)s', '%(divid)s', '%(vid)s', '%(default)s');" %
-                                          event_data),
-                                form_buttons=[SubmitButton(),
-                                              Button(stdmsgs.BUTTON_CANCEL,
-                                                     onclick="cancelInlineEdit(%s,\'%s\',\'%s\')" %\
-                                                         (entity.eid, rtype, divid))])
+        onsubmit = ("return inlineValidateRelationForm('%(divid)s-form', '%(rtype)s', "
+                    "'%(role)s', '%(eid)s', '%(divid)s', '%(vid)s', '%(default)s');"
+                    % event_data)
+        cancelclick = "cancelInlineEdit(%s,\'%s\',\'%s\')" % (
+            entity.eid, rtype, divid)
+        form = self.vreg.select_object('forms', 'base', self.req, entity=entity,
+                                       domid='%s-form' % divid, cssstyle='display: none',
+                                       onsubmit=onsubmit, action='#',
+                                       form_buttons=[SubmitButton(),
+                                                     Button(stdmsgs.BUTTON_CANCEL,
+                                                       onclick=cancelclick)])
         form.append_field(RelationField(name=rtype, role=role, sort=True,
                                         widget=Select(),
                                         label=u' '))
@@ -172,7 +178,6 @@
     __select__ = one_line_rset() & non_final_entity() & yes()
 
     title = _('edition')
-    renderer = EntityFormRenderer()
 
     def cell_call(self, row, col, **kwargs):
         entity = self.complete_entity(row, col)
@@ -185,7 +190,7 @@
                                        row=entity.row, col=entity.col, entity=entity,
                                        submitmsg=self.submited_message())
         self.init_form(form, entity)
-        self.w(form.form_render(renderer=self.renderer, formvid=u'edition'))
+        self.w(form.form_render(formvid=u'edition'))
 
     def init_form(self, form, entity):
         """customize your form before rendering here"""
@@ -289,7 +294,7 @@
         return self.req._('entity copied')
 
 
-class TableEditForm(CompositeForm):
+class TableEditForm(forms.CompositeForm):
     id = 'muledit'
     domid = 'entityForm'
     onsubmit = "return validateForm('%s', null);" % domid
@@ -319,7 +324,7 @@
         """
         #self.form_title(entity)
         form = self.vreg.select_object('forms', self.id, self.req, self.rset)
-        self.w(form.form_render(renderer=EntityCompositeFormRenderer()))
+        self.w(form.form_render())
 
 
 class InlineEntityEditionFormView(FormViewMixIn, EntityView):
@@ -350,13 +355,15 @@
     def render_form(self, entity, peid, rtype, role, **kwargs):
         """fetch and render the form"""
         form = self.vreg.select_object('forms', 'edition', self.req, None,
-                                       entity=entity, set_error_url=False)
+                                       entity=entity, form_renderer_id='inline',
+                                       set_error_url=False,
+                                       copy_nav_params=False)
         self.add_hiddens(form, entity, peid, rtype, role)
         divid = '%s-%s-%s' % (peid, rtype, entity.eid)
         title = self.schema.rschema(rtype).display_name(self.req, role)
         removejs = self.removejs % (peid, rtype,entity.eid)
-        self.w(form.form_render(renderer=EntityInlinedFormRenderer(), divid=divid,
-                                title=title, removejs=removejs,**kwargs))
+        self.w(form.form_render(divid=divid, title=title, removejs=removejs,
+                                **kwargs))
 
     def add_hiddens(self, form, entity, peid, rtype, role):
         # to ease overriding (see cubes.vcsfile.views.forms for instance)
--- a/web/views/editviews.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/editviews.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Some views used to help to the edition process
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/web/views/emailaddress.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/emailaddress.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Specific views for email addresses entities
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/web/views/embedding.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/embedding.py	Wed Jun 03 09:15:20 2009 +0200
@@ -3,8 +3,9 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -29,7 +30,7 @@
     """template embeding an external web pages into CubicWeb web interface
     """
     id = 'external'
-    
+
     def call(self, body):
         # XXX fallback to HTML 4 mode when embeding ?
         self.set_request_content_type()
@@ -92,12 +93,12 @@
     """
     id = 'embed'
     __select__ = (one_line_rset() & match_search_state('normal')
-                  & implements(IEmbedable) 
+                  & implements(IEmbedable)
                   & score_entity(entity_has_embedable_url))
-    
+
     title = _('embed')
     controller = 'embed'
-    
+
     def url(self, row=0):
         entity = self.rset.get_entity(row, 0)
         url = urljoin(self.req.base_url(), entity.embeded_url())
@@ -119,7 +120,7 @@
     def __init__(self, prefix, custom_css=None):
         self.prefix = prefix
         self.custom_css = custom_css
-        
+
     def __call__(self, match):
         original_url = match.group(1)
         url = self.prefix + urlquote(original_url, safe='')
@@ -136,7 +137,7 @@
         self.embedded_url = embedded_url
         self.tag = tag
         self.custom_css = custom_css
-    
+
     def __call__(self, match):
         original_url = match.group(1)
         if '://' in original_url:
--- a/web/views/error.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/error.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 as startup views and are used for standard error pages (404, 500, etc.)
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -24,5 +25,3 @@
         _ = self.req._
         self.w(u"<h1>%s</h1>" %
                _('an error occured, the request cannot be fulfilled'))
-    
-
--- a/web/views/facets.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/facets.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """the facets box and some basic facets
 
 :organization: Logilab
-:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -22,7 +23,7 @@
                          **kwargs):
     if view and getattr(view, 'filter_box_context_info', lambda: None)():
         return 1
-    return 0    
+    return 0
 
 
 class FilterBox(BoxTemplate):
@@ -42,7 +43,7 @@
         be used by the facet
         """
         return {}
-        
+
     def _get_context(self, view):
         context = getattr(view, 'filter_box_context_info', lambda: None)()
         if context:
@@ -52,7 +53,7 @@
             vid, divid = None, 'pageContent'
             paginate = view and view.need_navigation
         return rset, vid, divid, paginate
-        
+
     def call(self, view=None):
         req = self.req
         req.add_js( ('cubicweb.ajax.js', 'cubicweb.formfilter.js') )
@@ -109,7 +110,7 @@
         return self.vreg.possible_vobjects('facets', self.req, rset,
                                            context='facetbox',
                                            filtered_variable=mainvar)
-        
+
 # facets ######################################################################
 
 class CreatedByFacet(RelationFacet):
@@ -144,7 +145,7 @@
         """
         etypes = self.rset.column_types(0)
         return sorted((self.req._(etype), etype) for etype in etypes)
-    
+
     def add_rql_restrictions(self):
         """add restriction for this facet into the rql syntax tree"""
         value = self.req.form.get(self.id)
@@ -162,7 +163,7 @@
     @property
     def title(self):
         return self.req._('has_text')
-    
+
     def get_widget(self):
         """return the widget instance to use to display this facet
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/views/formrenderers.py	Wed Jun 03 09:15:20 2009 +0200
@@ -0,0 +1,501 @@
+"""form renderers, responsible to layout a form to html
+
+:organization: Logilab
+:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
+__docformat__ = "restructuredtext en"
+
+from logilab.common import dictattr
+from logilab.mtconverter import html_escape
+
+from simplejson import dumps
+
+from cubicweb.common import tags
+from cubicweb.appobject import AppRsetObject
+from cubicweb.selectors import entity_implements, yes
+from cubicweb.web import eid_param
+from cubicweb.web import formwidgets as fwdgs
+from cubicweb.web.widgets import checkbox
+from cubicweb.web.formfields import HiddenInitialValueField
+
+
+class FormRenderer(AppRsetObject):
+    """basic renderer displaying fields in a two columns table label | value
+
+    +--------------+--------------+
+    | field1 label | field1 input |
+    +--------------+--------------+
+    | field1 label | field2 input |
+    +--------------+--------------+
+    +---------+
+    | buttons |
+    +---------+
+    """
+    __registry__ = 'formrenderers'
+    id = 'default'
+
+    _options = ('display_fields', 'display_label', 'display_help',
+                'display_progress_div', 'table_class', 'button_bar_class',
+                # add entity since it may be given to select the renderer
+                'entity')
+    display_fields = None # None -> all fields
+    display_label = True
+    display_help = True
+    display_progress_div = True
+    table_class = u'attributeForm'
+    button_bar_class = u'formButtonBar'
+
+    def __init__(self, req=None, rset=None, row=None, col=None, **kwargs):
+        super(FormRenderer, self).__init__(req, rset, row, col)
+        if self._set_options(kwargs):
+            raise ValueError('unconsumed arguments %s' % kwargs)
+
+    def _set_options(self, kwargs):
+        for key in self._options:
+            try:
+                setattr(self, key, kwargs.pop(key))
+            except KeyError:
+                continue
+        return kwargs
+
+    # renderer interface ######################################################
+
+    def render(self, form, values):
+        self._set_options(values)
+        form.add_media()
+        data = []
+        w = data.append
+        w(self.open_form(form, values))
+        if self.display_progress_div:
+            w(u'<div id="progress">%s</div>' % self.req._('validating...'))
+        w(u'<fieldset>')
+        w(tags.input(type=u'hidden', name=u'__form_id',
+                     value=values.get('formvid', form.id)))
+        if form.redirect_path:
+            w(tags.input(type='hidden', name='__redirectpath', value=form.redirect_path))
+        self.render_fields(w, form, values)
+        self.render_buttons(w, form)
+        w(u'</fieldset>')
+        w(u'</form>')
+        errormsg = self.error_message(form)
+        if errormsg:
+            data.insert(0, errormsg)
+        return '\n'.join(data)
+
+    def render_label(self, form, field):
+        label = self.req._(field.label)
+        attrs = {'for': form.context[field]['id']}
+        if field.required:
+            attrs['class'] = 'required'
+        return tags.label(label, **attrs)
+
+    def render_help(self, form, field):
+        help = []
+        descr = field.help
+        if descr:
+            help.append('<div class="helper">%s</div>' % self.req._(descr))
+        example = field.example_format(self.req)
+        if example:
+            help.append('<div class="helper">(%s: %s)</div>'
+                        % (self.req._('sample format'), example))
+        return u'&nbsp;'.join(help)
+
+    # specific methods (mostly to ease overriding) #############################
+
+    def error_message(self, form):
+        """return formatted error message
+
+        This method should be called once inlined field errors has been consumed
+        """
+        req = self.req
+        errex = form.form_valerror
+        # get extra errors
+        if errex is not None:
+            errormsg = req._('please correct the following errors:')
+            displayed = form.form_displayed_errors
+            errors = sorted((field, err) for field, err in errex.errors.items()
+                            if not field in displayed)
+            if errors:
+                if len(errors) > 1:
+                    templstr = '<li>%s</li>\n'
+                else:
+                    templstr = '&nbsp;%s\n'
+                for field, err in errors:
+                    if field is None:
+                        errormsg += templstr % err
+                    else:
+                        errormsg += templstr % '%s: %s' % (req._(field), err)
+                if len(errors) > 1:
+                    errormsg = '<ul>%s</ul>' % errormsg
+            return u'<div class="errorMessage">%s</div>' % errormsg
+        return u''
+
+    def open_form(self, form, values):
+        if form.form_needs_multipart:
+            enctype = 'multipart/form-data'
+        else:
+            enctype = 'application/x-www-form-urlencoded'
+        if form.action is None:
+            action = self.req.build_url('edit')
+        else:
+            action = form.action
+        tag = ('<form action="%s" method="post" enctype="%s"' % (
+            html_escape(action or '#'), enctype))
+        if form.domid:
+            tag += ' id="%s"' % form.domid
+        if form.onsubmit:
+            tag += ' onsubmit="%s"' % html_escape(form.onsubmit % dictattr(form))
+        if form.cssstyle:
+            tag += ' style="%s"' % html_escape(form.cssstyle)
+        if form.cssclass:
+            tag += ' class="%s"' % html_escape(form.cssclass)
+        if form.cwtarget:
+            tag += ' cubicweb:target="%s"' % html_escape(form.cwtarget)
+        return tag + '>'
+
+    def display_field(self, form, field):
+        if isinstance(field, HiddenInitialValueField):
+            field = field.visible_field
+        return (self.display_fields is None
+                or field.name in form.internal_fields
+                or (field.name, field.role) in self.display_fields
+                or (field.name, field.role) in form.internal_fields)
+
+    def render_fields(self, w, form, values):
+        form.form_build_context(values)
+        fields = self._render_hidden_fields(w, form)
+        if fields:
+            self._render_fields(fields, w, form)
+        self.render_child_forms(w, form, values)
+
+    def render_child_forms(self, w, form, values):
+        # render
+        for childform in getattr(form, 'forms', []):
+            self.render_fields(w, childform, values)
+
+    def _render_hidden_fields(self, w, form):
+        fields = form.fields[:]
+        for field in form.fields:
+            if not self.display_field(form, field):
+                fields.remove(field)
+            elif not field.is_visible():
+                w(field.render(form, self))
+                fields.remove(field)
+        return fields
+
+    def _render_fields(self, fields, w, form):
+        w(u'<table class="%s">' % self.table_class)
+        for field in fields:
+            w(u'<tr>')
+            if self.display_label:
+                w(u'<th class="labelCol">%s</th>' % self.render_label(form, field))
+            error = form.form_field_error(field)
+            if error:
+                w(u'<td class="error">')
+                w(error)
+            else:
+                w(u'<td>')
+            w(field.render(form, self))
+            if self.display_help:
+                w(self.render_help(form, field))
+            w(u'</td></tr>')
+        w(u'</table>')
+
+    def render_buttons(self, w, form):
+        w(u'<table class="%s">\n<tr>\n' % self.button_bar_class)
+        for button in form.form_buttons:
+            w(u'<td>%s</td>\n' % button.render(form))
+        w(u'</tr></table>')
+
+
+class BaseFormRenderer(FormRenderer):
+    """use form_renderer_id = 'base' if you want base FormRenderer without
+    adaptation by selection
+    """
+    id = 'base'
+
+
+class HTableFormRenderer(FormRenderer):
+    """display fields horizontally in a table
+
+    +--------------+--------------+---------+
+    | field1 label | field2 label |         |
+    +--------------+--------------+---------+
+    | field1 input | field2 input | buttons
+    +--------------+--------------+---------+
+    """
+    id = 'htable'
+
+    display_help = False
+    def _render_fields(self, fields, w, form):
+        w(u'<table border="0">')
+        w(u'<tr>')
+        for field in fields:
+            if self.display_label:
+                w(u'<th class="labelCol">%s</th>' % self.render_label(form, field))
+            if self.display_help:
+                w(self.render_help(form, field))
+        # empty slot for buttons
+        w(u'<th class="labelCol">&nbsp;</th>')
+        w(u'</tr>')
+        w(u'<tr>')
+        for field in fields:
+            error = form.form_field_error(field)
+            if error:
+                w(u'<td class="error">')
+                w(error)
+            else:
+                w(u'<td>')
+            w(field.render(form, self))
+            w(u'</td>')
+        w(u'<td>')
+        for button in form.form_buttons:
+            w(button.render(form))
+        w(u'</td>')
+        w(u'</tr>')
+        w(u'</table>')
+
+    def render_buttons(self, w, form):
+        pass
+
+
+class EntityCompositeFormRenderer(FormRenderer):
+    """specific renderer for multiple entities edition form (muledit)"""
+    id = 'composite'
+
+    def render_fields(self, w, form, values):
+        if not form.is_subform:
+            w(u'<table class="listing">')
+        super(EntityCompositeFormRenderer, self).render_fields(w, form, values)
+        if not form.is_subform:
+            w(u'</table>')
+
+    def _render_fields(self, fields, w, form):
+        if form.is_subform:
+            entity = form.edited_entity
+            values = form.form_previous_values
+            qeid = eid_param('eid', entity.eid)
+            cbsetstate = "setCheckboxesState2('eid', %s, 'checked')" % html_escape(dumps(entity.eid))
+            w(u'<tr class="%s">' % (entity.row % 2 and u'even' or u'odd'))
+            # XXX turn this into a widget used on the eid field
+            w(u'<td>%s</td>' % checkbox('eid', entity.eid, checked=qeid in values))
+            for field in fields:
+                error = form.form_field_error(field)
+                if error:
+                    w(u'<td class="error">')
+                    w(error)
+                else:
+                    w(u'<td>')
+                if isinstance(field.widget, (fwdgs.Select, fwdgs.CheckBox, fwdgs.Radio)):
+                    field.widget.attrs['onchange'] = cbsetstate
+                elif isinstance(field.widget, fwdgs.Input):
+                    field.widget.attrs['onkeypress'] = cbsetstate
+                w(u'<div>%s</div>' % field.render(form, self))
+                w(u'</td>')
+        else:
+            # main form, display table headers
+            w(u'<tr class="header">')
+            w(u'<th align="left">%s</th>'
+              % tags.input(type='checkbox', title=self.req._('toggle check boxes'),
+                           onclick="setCheckboxesState('eid', this.checked)"))
+            for field in self.forms[0].fields:
+                if self.display_field(form, field) and field.is_visible():
+                    w(u'<th>%s</th>' % self.req._(field.label))
+        w(u'</tr>')
+
+
+class EntityFormRenderer(FormRenderer):
+    """specific renderer for entity edition form (edition)"""
+    __select__ = entity_implements('Any') & yes()
+
+    _options = FormRenderer._options + ('display_relations_form',)
+    display_relations_form = True
+
+    def render(self, form, values):
+        rendered = super(EntityFormRenderer, self).render(form, values)
+        return rendered + u'</div>' # close extra div introducted by open_form
+
+    def open_form(self, form, values):
+        attrs_fs_label = ('<div class="iformTitle"><span>%s</span></div>'
+                          % self.req._('main informations'))
+        attrs_fs_label += '<div class="formBody">'
+        return attrs_fs_label + super(EntityFormRenderer, self).open_form(form, values)
+
+    def render_fields(self, w, form, values):
+        super(EntityFormRenderer, self).render_fields(w, form, values)
+        self.inline_entities_form(w, form)
+        if form.edited_entity.has_eid() and self.display_relations_form:
+            self.relations_form(w, form)
+
+    def _render_fields(self, fields, w, form):
+        if not form.edited_entity.has_eid() or form.edited_entity.has_perm('update'):
+            super(EntityFormRenderer, self)._render_fields(fields, w, form)
+
+    def render_buttons(self, w, form):
+        if len(form.form_buttons) == 3:
+            w("""<table width="100%%">
+  <tbody>
+   <tr><td align="center">
+     %s
+   </td><td style="align: right; width: 50%%;">
+     %s
+     %s
+   </td></tr>
+  </tbody>
+ </table>""" % tuple(button.render(form) for button in form.form_buttons))
+        else:
+            super(EntityFormRenderer, self).render_buttons(w, form)
+
+    def relations_form(self, w, form):
+        srels_by_cat = form.srelations_by_category('generic', 'add')
+        if not srels_by_cat:
+            return u''
+        req = self.req
+        _ = req._
+        label = u'%s :' % _('This %s' % form.edited_entity.e_schema).capitalize()
+        eid = form.edited_entity.eid
+        w(u'<fieldset class="subentity">')
+        w(u'<legend class="iformTitle">%s</legend>' % label)
+        w(u'<table id="relatedEntities">')
+        for rschema, target, related in form.relations_table():
+            # already linked entities
+            if related:
+                w(u'<tr><th class="labelCol">%s</th>' % rschema.display_name(req, target))
+                w(u'<td>')
+                w(u'<ul>')
+                for viewparams in related:
+                    w(u'<li class="invisible">%s<div id="span%s" class="%s">%s</div></li>'
+                      % (viewparams[1], viewparams[0], viewparams[2], viewparams[3]))
+                if not form.force_display and form.maxrelitems < len(related):
+                    link = (u'<span class="invisible">'
+                            '[<a href="javascript: window.location.href+=\'&amp;__force_display=1\'">%s</a>]'
+                            '</span>' % self.req._('view all'))
+                    w(u'<li class="invisible">%s</li>' % link)
+                w(u'</ul>')
+                w(u'</td>')
+                w(u'</tr>')
+        pendings = list(form.restore_pending_inserts())
+        if not pendings:
+            w(u'<tr><th>&nbsp;</th><td>&nbsp;</td></tr>')
+        else:
+            for row in pendings:
+                # soon to be linked to entities
+                w(u'<tr id="tr%s">' % row[1])
+                w(u'<th>%s</th>' % row[3])
+                w(u'<td>')
+                w(u'<a class="handle" title="%s" href="%s">[x]</a>' %
+                  (_('cancel this insert'), row[2]))
+                w(u'<a id="a%s" class="editionPending" href="%s">%s</a>'
+                  % (row[1], row[4], html_escape(row[5])))
+                w(u'</td>')
+                w(u'</tr>')
+        w(u'<tr id="relationSelectorRow_%s" class="separator">' % eid)
+        w(u'<th class="labelCol">')
+        w(u'<span>%s</span>' % _('add relation'))
+        w(u'<select id="relationSelector_%s" tabindex="%s" '
+          'onchange="javascript:showMatchingSelect(this.options[this.selectedIndex].value,%s);">'
+          % (eid, req.next_tabindex(), html_escape(dumps(eid))))
+        w(u'<option value="">%s</option>' % _('select a relation'))
+        for i18nrtype, rschema, target in srels_by_cat:
+            # more entities to link to
+            w(u'<option value="%s_%s">%s</option>' % (rschema, target, i18nrtype))
+        w(u'</select>')
+        w(u'</th>')
+        w(u'<td id="unrelatedDivs_%s"></td>' % eid)
+        w(u'</tr>')
+        w(u'</table>')
+        w(u'</fieldset>')
+
+    def inline_entities_form(self, w, form):
+        """create a form to edit entity's inlined relations"""
+        if not hasattr(form, 'inlined_relations'):
+            return
+        entity = form.edited_entity
+        __ = self.req.__
+        for rschema, targettypes, role in form.inlined_relations():
+            # show inline forms only if there's one possible target type
+            # for rschema
+            if len(targettypes) != 1:
+                self.warning('entity related by the %s relation should have '
+                             'inlined form but there is multiple target types, '
+                             'dunno what to do', rschema)
+                continue
+            targettype = targettypes[0].type
+            if form.should_inline_relation_form(rschema, targettype, role):
+                w(u'<div id="inline%sslot">' % rschema)
+                existant = entity.has_eid() and entity.related(rschema)
+                if existant:
+                    # display inline-edition view for all existing related entities
+                    w(form.view('inline-edition', existant, rtype=rschema, role=role,
+                                ptype=entity.e_schema, peid=entity.eid))
+                if role == 'subject':
+                    card = rschema.rproperty(entity.e_schema, targettype, 'cardinality')[0]
+                else:
+                    card = rschema.rproperty(targettype, entity.e_schema, 'cardinality')[1]
+                # there is no related entity and we need at least one: we need to
+                # display one explicit inline-creation view
+                if form.should_display_inline_creation_form(rschema, existant, card):
+                    w(form.view('inline-creation', None, etype=targettype,
+                                peid=entity.eid, ptype=entity.e_schema,
+                                rtype=rschema, role=role))
+                # we can create more than one related entity, we thus display a link
+                # to add new related entities
+                if form.should_display_add_new_relation_link(rschema, existant, card):
+                    divid = "addNew%s%s%s:%s" % (targettype, rschema, role, entity.eid)
+                    w(u'<div class="inlinedform" id="%s" cubicweb:limit="true">'
+                      % divid)
+                    js = "addInlineCreationForm('%s', '%s', '%s', '%s')" % (
+                        entity.eid, targettype, rschema, role)
+                    if card in '1?':
+                        js = "toggleVisibility('%s'); %s" % (divid, js)
+                    w(u'<a class="addEntity" id="add%s:%slink" href="javascript: %s" >+ %s.</a>'
+                      % (rschema, entity.eid, js, __('add a %s' % targettype)))
+                    w(u'</div>')
+                    w(u'<div class="trame_grise">&nbsp;</div>')
+                w(u'</div>')
+
+
+class EntityInlinedFormRenderer(EntityFormRenderer):
+    """specific renderer for entity inlined edition form
+    (inline-[creation|edition])
+    """
+    id = 'inline'
+
+    def render(self, form, values):
+        form.add_media()
+        data = []
+        w = data.append
+        try:
+            w(u'<div id="div-%(divid)s" onclick="%(divonclick)s">' % values)
+        except KeyError:
+            w(u'<div id="div-%(divid)s">' % values)
+        else:
+            w(u'<div id="notice-%s" class="notice">%s</div>' % (
+                values['divid'], self.req._('click on the box to cancel the deletion')))
+        w(u'<div class="iformBody">')
+        values['removemsg'] = self.req.__('remove this %s' % form.edited_entity.e_schema)
+        w(u'<div class="iformTitle"><span>%(title)s</span> '
+          '#<span class="icounter">1</span> '
+          '[<a href="javascript: %(removejs)s;noop();">%(removemsg)s</a>]</div>'
+          % values)
+        # cleanup values
+        for key in ('title', 'removejs', 'removemsg'):
+            values.pop(key)
+        self.render_fields(w, form, values)
+        w(u'</div></div>')
+        return '\n'.join(data)
+
+    def render_fields(self, w, form, values):
+        form.form_build_context(values)
+        w(u'<fieldset id="fs-%(divid)s">' % values)
+        fields = self._render_hidden_fields(w, form)
+        w(u'</fieldset>')
+        w(u'<fieldset class="subentity">')
+        if fields:
+            self._render_fields(fields, w, form)
+        self.render_child_forms(w, form, values)
+        self.inline_entities_form(w, form)
+        w(u'</fieldset>')
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/views/forms.py	Wed Jun 03 09:15:20 2009 +0200
@@ -0,0 +1,524 @@
+"""some base form classes for CubicWeb web client
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
+__docformat__ = "restructuredtext en"
+
+from warnings import warn
+
+from logilab.common.compat import any
+from logilab.common.decorators import iclassmethod
+
+from cubicweb.selectors import non_final_entity, match_kwargs, one_line_rset
+from cubicweb.web import INTERNAL_FIELD_VALUE, eid_param
+from cubicweb.web import form, formwidgets as fwdgs
+from cubicweb.web.controller import NAV_FORM_PARAMETERS
+from cubicweb.web.formfields import HiddenInitialValueField, StringField
+
+
+class FieldsForm(form.Form):
+    id = 'base'
+
+    is_subform = False
+
+    # attributes overrideable through __init__
+    internal_fields = ('__errorurl',) + NAV_FORM_PARAMETERS
+    needs_js = ('cubicweb.ajax.js', 'cubicweb.edition.js',)
+    needs_css = ('cubicweb.form.css',)
+    domid = 'form'
+    title = None
+    action = None
+    onsubmit = "return freezeFormButtons('%(domid)s');"
+    cssclass = None
+    cssstyle = None
+    cwtarget = None
+    redirect_path = None
+    set_error_url = True
+    copy_nav_params = False
+    form_buttons = None # form buttons (button widgets instances)
+    form_renderer_id = 'default'
+
+    def __init__(self, req, rset=None, row=None, col=None, submitmsg=None,
+                 **kwargs):
+        super(FieldsForm, self).__init__(req, rset, row=row, col=col)
+        self.fields = list(self.__class__._fields_)
+        for key, val in kwargs.items():
+            if key in NAV_FORM_PARAMETERS:
+                self.form_add_hidden(key, val)
+            else:
+                assert hasattr(self.__class__, key) and not key[0] == '_', key
+                setattr(self, key, val)
+        if self.set_error_url:
+            self.form_add_hidden('__errorurl', self.session_key())
+        if self.copy_nav_params:
+            for param in NAV_FORM_PARAMETERS:
+                if not param in kwargs:
+                    value = req.form.get(param)
+                    if value:
+                        self.form_add_hidden(param, value)
+        if submitmsg is not None:
+            self.form_add_hidden('__message', submitmsg)
+        self.context = None
+        if 'domid' in kwargs:# session key changed
+            self.restore_previous_post(self.session_key())
+
+    @iclassmethod
+    def _fieldsattr(cls_or_self):
+        if isinstance(cls_or_self, type):
+            fields = cls_or_self._fields_
+        else:
+            fields = cls_or_self.fields
+        return fields
+
+    @iclassmethod
+    def field_by_name(cls_or_self, name, role='subject'):
+        """return field with the given name and role.
+        Raise FieldNotFound if the field can't be found.
+        """
+        for field in cls_or_self._fieldsattr():
+            if field.name == name and field.role == role:
+                return field
+        raise form.FieldNotFound(name)
+
+    @iclassmethod
+    def fields_by_name(cls_or_self, name, role='subject'):
+        """return a list of fields with the given name and role"""
+        return [field for field in cls_or_self._fieldsattr()
+                if field.name == name and field.role == role]
+
+    @iclassmethod
+    def remove_field(cls_or_self, field):
+        """remove a field from form class or instance"""
+        cls_or_self._fieldsattr().remove(field)
+
+    @iclassmethod
+    def append_field(cls_or_self, field):
+        """append a field to form class or instance"""
+        cls_or_self._fieldsattr().append(field)
+
+    @iclassmethod
+    def insert_field_before(cls_or_self, new_field, name, role='subject'):
+        field = cls_or_self.field_by_name(name, role)
+        fields = cls_or_self._fieldsattr()
+        fields.insert(fields.index(field), new_field)
+
+    @iclassmethod
+    def insert_field_after(cls_or_self, new_field, name, role='subject'):
+        field = cls_or_self.field_by_name(name, role)
+        fields = cls_or_self._fieldsattr()
+        fields.insert(fields.index(field)+1, new_field)
+
+    @property
+    def form_needs_multipart(self):
+        """true if the form needs enctype=multipart/form-data"""
+        return any(field.needs_multipart for field in self.fields)
+
+    def form_add_hidden(self, name, value=None, **kwargs):
+        """add an hidden field to the form"""
+        field = StringField(name=name, widget=fwdgs.HiddenInput, initial=value,
+                            **kwargs)
+        if 'id' in kwargs:
+            # by default, hidden input don't set id attribute. If one is
+            # explicitly specified, ensure it will be set
+            field.widget.setdomid = True
+        self.append_field(field)
+        return field
+
+    def add_media(self):
+        """adds media (CSS & JS) required by this widget"""
+        if self.needs_js:
+            self.req.add_js(self.needs_js)
+        if self.needs_css:
+            self.req.add_css(self.needs_css)
+
+    def form_render(self, **values):
+        """render this form, using the renderer given in args or the default
+        FormRenderer()
+        """
+        renderer = values.pop('renderer', None)
+        if renderer is None:
+            renderer = self.form_default_renderer()
+        return renderer.render(self, values)
+
+    def form_default_renderer(self):
+        return self.vreg.select_object('formrenderers', self.form_renderer_id,
+                                       self.req, self.rset,
+                                       row=self.row, col=self.col)
+
+    def form_build_context(self, rendervalues=None):
+        """build form context values (the .context attribute which is a
+        dictionary with field instance as key associated to a dictionary
+        containing field 'name' (qualified), 'id', 'value' (for display, always
+        a string).
+
+        rendervalues is an optional dictionary containing extra kwargs given to
+        form_render()
+        """
+        self.context = context = {}
+        # ensure rendervalues is a dict
+        if rendervalues is None:
+            rendervalues = {}
+        # use a copy in case fields are modified while context is build (eg
+        # __linkto handling for instance)
+        for field in self.fields[:]:
+            for field in field.actual_fields(self):
+                field.form_init(self)
+                value = self.form_field_display_value(field, rendervalues)
+                context[field] = {'value': value,
+                                  'name': self.form_field_name(field),
+                                  'id': self.form_field_id(field),
+                                  }
+
+    def form_field_display_value(self, field, rendervalues, load_bytes=False):
+        """return field's *string* value to use for display
+
+        looks in
+        1. previously submitted form values if any (eg on validation error)
+        2. req.form
+        3. extra kw args given to render_form
+        4. field's typed value
+
+        values found in 1. and 2. are expected te be already some 'display'
+        value while those found in 3. and 4. are expected to be correctly typed.
+        """
+        value = self._req_display_value(field)
+        if value is None:
+            if field.name in rendervalues:
+                value = rendervalues[field.name]
+            else:
+                value = self.form_field_value(field, load_bytes)
+                if callable(value):
+                    value = value(self)
+            if value != INTERNAL_FIELD_VALUE:
+                value = field.format_value(self.req, value)
+        return value
+
+    def _req_display_value(self, field):
+        qname = self.form_field_name(field)
+        if qname in self.form_previous_values:
+            return self.form_previous_values[qname]
+        if qname in self.req.form:
+            return self.req.form[qname]
+        if field.name in self.req.form:
+            return self.req.form[field.name]
+        return None
+
+    def form_field_value(self, field, load_bytes=False):
+        """return field's *typed* value"""
+        myattr = '%s_%s_default' % (field.role, field.name)
+        if hasattr(self, myattr):
+            return getattr(self, myattr)()
+        value = field.initial
+        if callable(value):
+            value = value(self)
+        return value
+
+    def form_field_error(self, field):
+        """return validation error for widget's field, if any"""
+        if self._field_has_error(field):
+            self.form_displayed_errors.add(field.name)
+            return u'<span class="error">%s</span>' % self.form_valerror.errors[field.name]
+        return u''
+
+    def form_field_format(self, field):
+        """return MIME type used for the given (text or bytes) field"""
+        return self.req.property_value('ui.default-text-format')
+
+    def form_field_encoding(self, field):
+        """return encoding used for the given (text) field"""
+        return self.req.encoding
+
+    def form_field_name(self, field):
+        """return qualified name for the given field"""
+        return field.name
+
+    def form_field_id(self, field):
+        """return dom id for the given field"""
+        return field.id
+
+    def form_field_vocabulary(self, field, limit=None):
+        """return vocabulary for the given field. Should be overriden in
+        specific forms using fields which requires some vocabulary
+        """
+        raise NotImplementedError
+
+    def _field_has_error(self, field):
+        """return true if the field has some error in given validation exception
+        """
+        return self.form_valerror and field.name in self.form_valerror.errors
+
+
+class EntityFieldsForm(FieldsForm):
+    id = 'base'
+    __select__ = (match_kwargs('entity') | (one_line_rset & non_final_entity()))
+
+    internal_fields = FieldsForm.internal_fields + ('__type', 'eid', '__maineid')
+    domid = 'entityForm'
+
+    def __init__(self, *args, **kwargs):
+        self.edited_entity = kwargs.pop('entity', None)
+        msg = kwargs.pop('submitmsg', None)
+        super(EntityFieldsForm, self).__init__(*args, **kwargs)
+        if self.edited_entity is None:
+            self.edited_entity = self.complete_entity(self.row or 0, self.col or 0)
+        self.form_add_hidden('__type', eidparam=True)
+        self.form_add_hidden('eid')
+        if msg:
+            # If we need to directly attach the new object to another one
+            self.form_add_hidden('__message', msg)
+        if not self.is_subform:
+            for linkto in self.req.list_form_param('__linkto'):
+                self.form_add_hidden('__linkto', linkto)
+                msg = '%s %s' % (msg, self.req._('and linked'))
+
+    def _field_has_error(self, field):
+        """return true if the field has some error in given validation exception
+        """
+        return super(EntityFieldsForm, self)._field_has_error(field) \
+               and self.form_valerror.eid == self.edited_entity.eid
+
+    def _relation_vocabulary(self, rtype, targettype, role,
+                            limit=None, done=None):
+        """return unrelated entities for a given relation and target entity type
+        for use in vocabulary
+        """
+        if done is None:
+            done = set()
+        rset = self.edited_entity.unrelated(rtype, targettype, role, limit)
+        res = []
+        for entity in rset.entities():
+            if entity.eid in done:
+                continue
+            done.add(entity.eid)
+            res.append((entity.view('combobox'), entity.eid))
+        return res
+
+    def _req_display_value(self, field):
+        value = super(EntityFieldsForm, self)._req_display_value(field)
+        if value is None:
+            value = self.edited_entity.linked_to(field.name, field.role)
+            if value:
+                searchedvalues = ['%s:%s:%s' % (field.name, eid, field.role)
+                                  for eid in value]
+                # remove associated __linkto hidden fields
+                for field in self.fields_by_name('__linkto'):
+                    if field.initial in searchedvalues:
+                        self.remove_field(field)
+            else:
+                value = None
+        return value
+
+    def _form_field_default_value(self, field, load_bytes):
+        defaultattr = 'default_%s' % field.name
+        if hasattr(self.edited_entity, defaultattr):
+            # XXX bw compat, default_<field name> on the entity
+            warn('found %s on %s, should be set on a specific form'
+                 % (defaultattr, self.edited_entity.id), DeprecationWarning)
+            value = getattr(self.edited_entity, defaultattr)
+            if callable(value):
+                value = value()
+        else:
+            value = super(EntityFieldsForm, self).form_field_value(field,
+                                                                   load_bytes)
+        return value
+
+    def form_default_renderer(self):
+        return self.vreg.select_object('formrenderers', self.form_renderer_id,
+                                       self.req, self.rset,
+                                       row=self.row, col=self.col,
+                                       entity=self.edited_entity)
+
+    def form_build_context(self, values=None):
+        """overriden to add edit[s|o] hidden fields and to ensure schema fields
+        have eidparam set to True
+
+        edit[s|o] hidden fields are used to indicate the value for the
+        associated field before the (potential) modification made when
+        submitting the form.
+        """
+        eschema = self.edited_entity.e_schema
+        for field in self.fields[:]:
+            for field in field.actual_fields(self):
+                fieldname = field.name
+                if fieldname != 'eid' and (
+                    (eschema.has_subject_relation(fieldname) or
+                     eschema.has_object_relation(fieldname))):
+                    field.eidparam = True
+                    self.fields.append(HiddenInitialValueField(field))
+        return super(EntityFieldsForm, self).form_build_context(values)
+
+    def form_field_value(self, field, load_bytes=False):
+        """return field's *typed* value
+
+        overriden to deal with
+        * special eid / __type / edits- / edito- fields
+        * lookup for values on edited entities
+        """
+        attr = field.name
+        entity = self.edited_entity
+        if attr == 'eid':
+            return entity.eid
+        if not field.eidparam:
+            return super(EntityFieldsForm, self).form_field_value(field, load_bytes)
+        if attr.startswith('edits-') or attr.startswith('edito-'):
+            # edit[s|o]- fieds must have the actual value stored on the entity
+            assert hasattr(field, 'visible_field')
+            vfield = field.visible_field
+            assert vfield.eidparam
+            if entity.has_eid():
+                return self.form_field_value(vfield)
+            return INTERNAL_FIELD_VALUE
+        if attr == '__type':
+            return entity.id
+        if self.schema.rschema(attr).is_final():
+            attrtype = entity.e_schema.destination(attr)
+            if attrtype == 'Password':
+                return entity.has_eid() and INTERNAL_FIELD_VALUE or ''
+            if attrtype == 'Bytes':
+                if entity.has_eid():
+                    if load_bytes:
+                        return getattr(entity, attr)
+                    # XXX value should reflect if some file is already attached
+                    return True
+                return False
+            if entity.has_eid() or attr in entity:
+                value = getattr(entity, attr)
+            else:
+                value = self._form_field_default_value(field, load_bytes)
+            return value
+        # non final relation field
+        if entity.has_eid() or entity.relation_cached(attr, field.role):
+            value = [r[0] for r in entity.related(attr, field.role)]
+        else:
+            value = self._form_field_default_value(field, load_bytes)
+        return value
+
+    def form_field_format(self, field):
+        """return MIME type used for the given (text or bytes) field"""
+        entity = self.edited_entity
+        if field.eidparam and entity.e_schema.has_metadata(field.name, 'format') and (
+            entity.has_eid() or '%s_format' % field.name in entity):
+            return self.edited_entity.attr_metadata(field.name, 'format')
+        return self.req.property_value('ui.default-text-format')
+
+    def form_field_encoding(self, field):
+        """return encoding used for the given (text) field"""
+        entity = self.edited_entity
+        if field.eidparam and entity.e_schema.has_metadata(field.name, 'encoding') and (
+            entity.has_eid() or '%s_encoding' % field.name in entity):
+            return self.edited_entity.attr_metadata(field.name, 'encoding')
+        return super(EntityFieldsForm, self).form_field_encoding(field)
+
+    def form_field_name(self, field):
+        """return qualified name for the given field"""
+        if field.eidparam:
+            return eid_param(field.name, self.edited_entity.eid)
+        return field.name
+
+    def form_field_id(self, field):
+        """return dom id for the given field"""
+        if field.eidparam:
+            return eid_param(field.id, self.edited_entity.eid)
+        return field.id
+
+    def form_field_vocabulary(self, field, limit=None):
+        """return vocabulary for the given field"""
+        role, rtype = field.role, field.name
+        method = '%s_%s_vocabulary' % (role, rtype)
+        try:
+            vocabfunc = getattr(self, method)
+        except AttributeError:
+            try:
+                # XXX bw compat, <role>_<rtype>_vocabulary on the entity
+                vocabfunc = getattr(self.edited_entity, method)
+            except AttributeError:
+                vocabfunc = getattr(self, '%s_relation_vocabulary' % role)
+            else:
+                warn('found %s on %s, should be set on a specific form'
+                     % (method, self.edited_entity.id), DeprecationWarning)
+        # NOTE: it is the responsibility of `vocabfunc` to sort the result
+        #       (direclty through RQL or via a python sort). This is also
+        #       important because `vocabfunc` might return a list with
+        #       couples (label, None) which act as separators. In these
+        #       cases, it doesn't make sense to sort results afterwards.
+        return vocabfunc(rtype, limit)
+
+    def subject_relation_vocabulary(self, rtype, limit=None):
+        """defaut vocabulary method for the given relation, looking for
+        relation's object entities (i.e. self is the subject)
+        """
+        entity = self.edited_entity
+        if isinstance(rtype, basestring):
+            rtype = entity.schema.rschema(rtype)
+        done = None
+        assert not rtype.is_final(), rtype
+        if entity.has_eid():
+            done = set(e.eid for e in getattr(entity, str(rtype)))
+        result = []
+        rsetsize = None
+        for objtype in rtype.objects(entity.e_schema):
+            if limit is not None:
+                rsetsize = limit - len(result)
+            result += self._relation_vocabulary(rtype, objtype, 'subject',
+                                                rsetsize, done)
+            if limit is not None and len(result) >= limit:
+                break
+        return result
+
+    def object_relation_vocabulary(self, rtype, limit=None):
+        """defaut vocabulary method for the given relation, looking for
+        relation's subject entities (i.e. self is the object)
+        """
+        entity = self.edited_entity
+        if isinstance(rtype, basestring):
+            rtype = entity.schema.rschema(rtype)
+        done = None
+        if entity.has_eid():
+            done = set(e.eid for e in getattr(entity, 'reverse_%s' % rtype))
+        result = []
+        rsetsize = None
+        for subjtype in rtype.subjects(entity.e_schema):
+            if limit is not None:
+                rsetsize = limit - len(result)
+            result += self._relation_vocabulary(rtype, subjtype, 'object',
+                                                rsetsize, done)
+            if limit is not None and len(result) >= limit:
+                break
+        return result
+
+    def subject_in_state_vocabulary(self, rtype, limit=None):
+        """vocabulary method for the in_state relation, looking for relation's
+        object entities (i.e. self is the subject) according to initial_state,
+        state_of and next_state relation
+        """
+        entity = self.edited_entity
+        if not entity.has_eid() or not entity.in_state:
+            # get the initial state
+            rql = 'Any S where S state_of ET, ET name %(etype)s, ET initial_state S'
+            rset = self.req.execute(rql, {'etype': str(entity.e_schema)})
+            if rset:
+                return [(rset.get_entity(0, 0).view('combobox'), rset[0][0])]
+            return []
+        results = []
+        for tr in entity.in_state[0].transitions(entity):
+            state = tr.destination_state[0]
+            results.append((state.view('combobox'), state.eid))
+        return sorted(results)
+
+
+class CompositeForm(FieldsForm):
+    """form composed for sub-forms"""
+    id = 'composite'
+    form_renderer_id = id
+
+    def __init__(self, *args, **kwargs):
+        super(CompositeForm, self).__init__(*args, **kwargs)
+        self.forms = []
+
+    def form_add_subform(self, subform):
+        """mark given form as a subform and append it"""
+        subform.is_subform = True
+        self.forms.append(subform)
--- a/web/views/ibreadcrumbs.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/ibreadcrumbs.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,10 +1,12 @@
 """navigation components definition for CubicWeb web client
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
+_ = unicode
 
 from logilab.mtconverter import html_escape
 
@@ -16,12 +18,11 @@
 from cubicweb.common.uilib import cut
 from cubicweb.web.component import EntityVComponent
 
-_ = unicode
 
 def bc_title(entity):
     textsize = entity.req.property_value('navigation.short-line-size')
     return html_escape(cut(entity.dc_title(), textsize))
-    
+
 
 class BreadCrumbEntityVComponent(EntityVComponent):
     id = 'breadcrumbs'
@@ -52,7 +53,7 @@
                 self.w(u"\n")
                 self.wpath_part(parent, entity, i == len(path) - 1)
             self.w(u'</span>')
-            
+
     def wpath_part(self, part, contextentity, last=False):
         if isinstance(part, Entity):
             if last and part.eid == contextentity.eid:
@@ -67,7 +68,7 @@
         else:
             textsize = self.req.property_value('navigation.short-line-size')
             self.w(cut(unicode(part), textsize))
-        
+
 
 class BreadCrumbComponent(BreadCrumbEntityVComponent):
     __registry__ = 'components'
@@ -80,7 +81,6 @@
 
     def cell_call(self, row, col):
         entity = self.entity(row, col)
-        desc = cut(entity.dc_description(), 50)
-        self.w(u'<a href="%s" title="%s">%s</a>' % (html_escape(entity.absolute_url()),
-                                                    html_escape(desc),
-                                                    bc_title(entity)))
+        desc = html_escape(cut(entity.dc_description(), 50))
+        self.w(u'<a href="%s" title="%s">%s</a>' % (
+            html_escape(entity.absolute_url()), desc, bc_title(entity)))
--- a/web/views/idownloadable.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/idownloadable.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Specific views for entities implementing IDownloadable
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 _ = unicode
@@ -26,7 +27,7 @@
 
 def download_box(w, entity, title=None, label=None):
     req = entity.req
-    w(u'<div class="sideRelated">')
+    w(u'<div class="sideBox">')
     if title is None:
         title = req._('download')
     w(u'<div class="sideBoxTitle downloadBoxTitle"><span>%s</span></div>'
--- a/web/views/igeocodable.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/igeocodable.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Specific views for entities implementing IGeocodable
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/web/views/iprogress.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/iprogress.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Specific views for entities implementing IProgress
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 __docformat__ = "restructuredtext en"
--- a/web/views/isioc.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/isioc.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Specific views for SIOC interfaces
 
 :organization: Logilab
-:copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/web/views/magicsearch.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/magicsearch.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 __docformat__ = "restructuredtext en"
--- a/web/views/management.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/management.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,27 +2,28 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 _ = unicode
 
 from logilab.mtconverter import html_escape
 
-from cubicweb.selectors import yes, none_rset, match_user_groups
+from cubicweb.selectors import yes, none_rset, match_user_groups, authenticated_user
 from cubicweb.view import AnyRsetView, StartupView, EntityView
 from cubicweb.common.uilib import html_traceback, rest_traceback
 from cubicweb.web import formwidgets
-from cubicweb.web.form import FieldsForm, EntityFieldsForm
 from cubicweb.web.formfields import guess_field
-from cubicweb.web.formrenderers import HTableFormRenderer
 
 SUBMIT_MSGID = _('Submit bug report')
 MAIL_SUBMIT_MSGID = _('Submit bug report by mail')
 
+
 class SecurityViewMixIn(object):
     """display security information for a given schema """
+
     def schema_definition(self, eschema, link=True,  access_types=None):
         w = self.w
         _ = self.req._
@@ -33,14 +34,14 @@
             _("permission"), _('granted to groups'), _('rql expressions')))
         for access_type in access_types:
             w(u'<tr>')
-            w(u'<td>%s</td>' % _('%s_perm' % access_type))
+            w(u'<td>%s</td>' % self.req.__('%s_perm' % access_type))
             groups = eschema.get_groups(access_type)
             l = []
             groups = [(_(group), group) for group in groups]
             for trad, group in sorted(groups):
                 if link:
                     l.append(u'<a href="%s" class="%s">%s</a><br/>' % (
-                    self.build_url('egroup/%s' % group), group, trad))
+                    self.build_url('cwgroup/%s' % group), group, trad))
                 else:
                     l.append(u'<div class="%s">%s</div>' % (group, trad))
             w(u'<td>%s</td>' % u''.join(l))
@@ -65,7 +66,10 @@
 class SecurityManagementView(EntityView, SecurityViewMixIn):
     """display security information for a given entity"""
     id = 'security'
+    __select__ = EntityView.__select__ & authenticated_user()
+
     title = _('security')
+
     def call(self):
         self.w(u'<div id="progress">%s</div>' % self.req._('validating...'))
         super(SecurityManagementView, self).call()
@@ -101,11 +105,13 @@
     def owned_by_edit_form(self, entity):
         self.w('<h3>%s</h3>' % self.req._('ownership'))
         msg = self.req._('ownerships have been changed')
-        form = EntityFieldsForm(self.req, None, entity=entity, submitmsg=msg,
-                                form_buttons=[formwidgets.SubmitButton()],
-                                domid='ownership%s' % entity.eid,
-                                __redirectvid='security',
-                                __redirectpath=entity.rest_path())
+        form = self.vreg.select_object('forms', 'base', self.req, entity=entity,
+                                       form_renderer_id='base',
+                                  submitmsg=msg,
+                                  form_buttons=[formwidgets.SubmitButton()],
+                                  domid='ownership%s' % entity.eid,
+                                  __redirectvid='security',
+                                  __redirectpath=entity.rest_path())
         field = guess_field(entity.e_schema, self.schema.rschema('owned_by'))
         form.append_field(field)
         self.w(form.form_render(display_progress_div=False))
@@ -158,11 +164,11 @@
         newperm = self.vreg.etype_class('CWPermission')(self.req, None)
         newperm.eid = self.req.varmaker.next()
         w(u'<p>%s</p>' % _('add a new permission'))
-        form = EntityFieldsForm(self.req, None, entity=newperm,
-                                form_buttons=[formwidgets.SubmitButton()],
-                                domid='reqperm%s' % entity.eid,
-                                __redirectvid='security',
-                                __redirectpath=entity.rest_path())
+        form = self.vreg.select_object('forms', 'base', self.req, entity=newperm,
+                                       form_buttons=[formwidgets.SubmitButton()],
+                                       domid='reqperm%s' % entity.eid,
+                                       __redirectvid='security',
+                                       __redirectpath=entity.rest_path())
         form.form_add_hidden('require_permission', entity.eid, role='object',
                              eidparam=True)
         permnames = getattr(entity, '__permissions__', None)
@@ -178,8 +184,9 @@
         form.append_field(field)
         field = guess_field(cwpermschema, self.schema.rschema('require_group'))
         form.append_field(field)
-        self.w(form.form_render(renderer=HTableFormRenderer(display_progress_div=False)))
-
+        renderer = self.select_object('formrenderers', 'htable', self.req,
+                                      display_progress_div=False)
+        self.w(form.form_render(renderer=renderer))
 
 
 class ErrorView(AnyRsetView):
@@ -235,7 +242,7 @@
         submiturl = self.config['submit-url']
         submitmail = self.config['submit-mail']
         if submiturl or submitmail:
-            form = FieldsForm(self.req, set_error_url=False)
+            form = self.select_object('forms', 'base', self.req, set_error_url=False)
             binfo = text_error_description(ex, excinfo, req, eversion, cversions)
             form.form_add_hidden('description', binfo)
             form.form_add_hidden('__bugreporting', '1')
@@ -271,6 +278,7 @@
     binfo += '\n'
     return binfo
 
+
 class ProcessInformationView(StartupView):
     id = 'info'
     __select__ = none_rset() & match_user_groups('managers')
--- a/web/views/massmailing.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/massmailing.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,10 +1,12 @@
 """Mass mailing form views
 
 :organization: Logilab
-:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
+_ = unicode
 
 import operator
 
@@ -13,12 +15,11 @@
 from cubicweb.view import EntityView
 from cubicweb.web import stdmsgs
 from cubicweb.web.action import Action
-from cubicweb.web.form import FieldsForm, FormViewMixIn
-from cubicweb.web.formrenderers import FormRenderer
+from cubicweb.web.form import FormViewMixIn
 from cubicweb.web.formfields import StringField
 from cubicweb.web.formwidgets import CheckBox, TextInput, AjaxWidget, ImgButton
+from cubicweb.web.views import forms, formrenderers
 
-_ = unicode
 
 class SendEmailAction(Action):
     id = 'sendemail'
@@ -36,18 +37,20 @@
                               **params)
 
 
-class MassMailingForm(FieldsForm):
+class MassMailingForm(forms.FieldsForm):
     id = 'massmailing'
 
     sender = StringField(widget=TextInput({'disabled': 'disabled'}), label=_('From:'))
     recipient = StringField(widget=CheckBox(), label=_('Recipients:'))
-    subject = StringField(label=_('Subject:'))
+    subject = StringField(label=_('Subject:'), max_length=256)
     mailbody = StringField(widget=AjaxWidget(wdgtype='TemplateTextField',
                                              inputid='mailbody'))
+
     form_buttons = [ImgButton('sendbutton', "javascript: $('#sendmail').submit()",
                               _('send email'), 'SEND_EMAIL_ICON'),
                     ImgButton('cancelbutton', "javascript: history.back()",
                               stdmsgs.BUTTON_CANCEL, 'CANCEL_EMAIL_ICON')]
+    form_renderer_id = id
 
     def form_field_vocabulary(self, field):
         if field.name == 'recipient':
@@ -78,7 +81,8 @@
             helpmsg, u'\n'.join(substs))
 
 
-class MassMailingFormRenderer(FormRenderer):
+class MassMailingFormRenderer(formrenderers.FormRenderer):
+    id = 'massmailing'
     button_bar_class = u'toolbar'
 
     def _render_fields(self, fields, w, form):
@@ -124,4 +128,4 @@
         from_addr = '%s <%s>' % (req.user.dc_title(), req.user.get_email())
         form = self.vreg.select_object('forms', 'massmailing', self.req, self.rset,
                                        action='sendmail', domid='sendmail')
-        self.w(form.form_render(sender=from_addr, renderer=MassMailingFormRenderer()))
+        self.w(form.form_render(sender=from_addr))
--- a/web/views/navigation.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/navigation.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """navigation components definition for CubicWeb web client
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/web/views/old_calendar.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/old_calendar.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """html calendar views
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 from datetime import date, time, timedelta
--- a/web/views/owl.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/owl.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """produces some Ontology Web Language schema and views
 
 :organization: Logilab
-:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -14,7 +15,7 @@
 
 _ = unicode
 
-OWL_CARD_MAP = {'1': '<rdf:type rdf:resource="&owl;FunctionalProperty"/>',                      
+OWL_CARD_MAP = {'1': '<rdf:type rdf:resource="&owl;FunctionalProperty"/>',
                 '?': '<owl:maxCardinality rdf:datatype="&xsd;int">1</owl:maxCardinality>',
                 '+': '<owl:minCardinality rdf:datatype="&xsd;int">1</owl:minCardinality>',
                 '*': ''
@@ -37,7 +38,7 @@
 <!DOCTYPE rdf:RDF [
         <!ENTITY owl "http://www.w3.org/2002/07/owl#" >
         <!ENTITY xsd "http://www.w3.org/2001/XMLSchema#" >
-]>        
+]>
 <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:xsd="http://www.w3.org/2001/XMLSchema#"
@@ -48,7 +49,7 @@
 
   <owl:Ontology rdf:about="">
     <rdfs:comment>
-    %(appid)s Cubicweb OWL Ontology                           
+    %(appid)s Cubicweb OWL Ontology
     </rdfs:comment>
   </owl:Ontology>'''
 
@@ -71,7 +72,7 @@
         self.visit_schema(skipmeta=skipmeta)
         if writeprefix:
             self.w(OWL_CLOSING_ROOT)
-        
+
     def visit_schema(self, skiprels=DEFAULT_SKIP_RELS, skipmeta=True):
         """get a layout for a whole schema"""
         entities = sorted([eschema for eschema in self.schema.entities()
@@ -89,8 +90,8 @@
 
     def visit_entityschema(self, eschema, skiprels=()):
         """get a layout for an entity OWL schema"""
-        self.w(u'<owl:Class rdf:ID="%s">'% eschema)         
-        self.w(u'<!-- relations -->')    
+        self.w(u'<owl:Class rdf:ID="%s">'% eschema)
+        self.w(u'<!-- relations -->')
         for rschema, targetschemas, role in eschema.relation_definitions():
             if rschema.type in skiprels:
                 continue
@@ -113,7 +114,7 @@
 ''' % (label, cardtag))
 
         self.w(u'<!-- attributes -->')
-              
+
         for rschema, aschema in eschema.attribute_definitions():
             if not (rschema.has_local_role('read') or rschema.has_perm(self.req, 'read')):
                 continue
@@ -125,10 +126,10 @@
    <owl:onProperty rdf:resource="#%s"/>
    <rdf:type rdf:resource="&owl;FunctionalProperty"/>
   </owl:Restriction>
-</rdfs:subClassOf>'''                         
+</rdfs:subClassOf>'''
                    % aname)
         self.w(u'</owl:Class>')
-    
+
     def visit_property_schema(self, eschema, skiprels=()):
         """get a layout for property entity OWL schema"""
         for rschema, targetschemas, role in eschema.relation_definitions():
@@ -141,7 +142,7 @@
                 self.w(u'''<owl:ObjectProperty rdf:ID="%s">
  <rdfs:domain rdf:resource="#%s"/>
  <rdfs:range rdf:resource="#%s"/>
-</owl:ObjectProperty>                                                
+</owl:ObjectProperty>
 ''' % (label, eschema, oeschema.type))
 
     def visit_property_object_schema(self, eschema):
@@ -157,15 +158,15 @@
 </owl:DatatypeProperty>'''
                    % (aname, eschema, OWL_TYPE_MAP[aschema.type]))
 
-            
+
 class OWLABOXView(EntityView):
     '''This view represents a part of the ABOX for a given entity.'''
-    
+
     id = 'owlabox'
     title = _('owlabox')
     templatable = False
     content_type = 'application/xml' # 'text/xml'
-    
+
     def call(self):
         self.w(OWL_OPENING_ROOT % {'appid': self.schema.name})
         for i in xrange(self.rset.rowcount):
@@ -175,9 +176,9 @@
     def cell_call(self, row, col, skiprels=DEFAULT_SKIP_RELS):
         self.wview('owlaboxitem', self.rset, row=row, col=col, skiprels=skiprels)
 
-        
+
 class OWLABOXItemView(EntityView):
-    '''This view represents a part of the ABOX for a given entity.'''    
+    '''This view represents a part of the ABOX for a given entity.'''
     id = 'owlaboxitem'
     templatable = False
     content_type = 'application/xml' # 'text/xml'
@@ -208,9 +209,9 @@
             if not (rschema.has_local_role('read') or rschema.has_perm(self.req, 'read')):
                 continue
             if role == 'object':
-                attr = 'reverse_%s' % rschema.type 
+                attr = 'reverse_%s' % rschema.type
             else:
-                attr = rschema.type        
+                attr = rschema.type
             for x in getattr(entity, attr):
                 self.w(u'<%s>%s %s</%s>' % (attr, x.id, x.eid, attr))
         self.w(u'</%s>'% eschema)
@@ -219,10 +220,9 @@
 class DownloadOWLSchemaAction(Action):
     id = 'download_as_owl'
     __select__ = none_rset() & match_view('schema')
-    
+
     category = 'mainactions'
     title = _('download schema as owl')
-   
+
     def url(self):
         return self.build_url('view', vid='owl')
-
--- a/web/views/plots.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/plots.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,104 +1,203 @@
+"""basic plot views
+
+:organization: Logilab
+:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
+__docformat__ = "restructuredtext en"
+
 import os
+import time
+
+from simplejson import dumps
 
 from logilab.common import flatten
+from logilab.mtconverter import html_escape
 
+from cubicweb.utils import make_uid, UStringIO, datetime2ticks
 from cubicweb.vregistry import objectify_selector
 from cubicweb.web.views import baseviews
 
 @objectify_selector
-def plot_selector(cls, req, rset, *args, **kwargs):
+def at_least_two_columns(cls, req, rset, *args, **kwargs):
+    if not rset:
+        return 0
+    return len(rset.rows[0]) >= 2
+
+@objectify_selector
+def all_columns_are_numbers(cls, req, rset, *args, **kwargs):
     """accept result set with at least one line and two columns of result
     all columns after second must be of numerical types"""
-    if rset is None:
-        return 0
-    if not len(rset):
-        return 0
-    if len(rset.rows[0]) < 2:
-        return 0
     for etype in rset.description[0]:
         if etype not in ('Int', 'Float'):
             return 0
     return 1
 
+@objectify_selector
+def second_column_is_number(cls, req, rset, *args, **kwargs):
+    etype = rset.description[0][1]
+    if etype not  in ('Int', 'Float'):
+        return 0
+    return 1
+
+@objectify_selector
+def columns_are_date_then_numbers(cls, req, rset, *args, **kwargs):
+    etypes = rset.description[0]
+    if etypes[0] not in ('Date', 'Datetime'):
+        return 0
+    for etype in etypes[1:]:
+        if etype not in ('Int', 'Float'):
+            return 0
+    return 1
+
+
+def filterout_nulls(abscissa, plot):
+    filtered = []
+    for x, y in zip(abscissa, plot):
+        if x is None or y is None:
+            continue
+        filtered.append( (x, y) )
+    return sorted(filtered)
+
+class PlotWidget(object):
+    # XXX refactor with cubicweb.web.views.htmlwidgets.HtmlWidget
+    def _initialize_stream(self, w=None):
+        if w:
+            self.w = w
+        else:
+            self._stream = UStringIO()
+            self.w = self._stream.write
+
+    def render(self, *args, **kwargs):
+        w = kwargs.pop('w', None)
+        self._initialize_stream(w)
+        self._render(*args, **kwargs)
+        if w is None:
+            return self._stream.getvalue()
+
+class FlotPlotWidget(PlotWidget):
+    """PlotRenderer widget using Flot"""
+    onload = u'''
+%(plotdefs)s
+jQuery.plot(jQuery("#%(figid)s"), [%(plotdata)s],
+    {points: {show: true},
+     lines: {show: true},
+     grid: {hoverable: true},
+     xaxis: {mode: %(mode)s}});
+jQuery("#%(figid)s").bind("plothover", onPlotHover);
+'''
+
+    def __init__(self, labels, plots, timemode=False):
+        self.labels = labels
+        self.plots = plots # list of list of couples
+        self.timemode = timemode
+
+    def dump_plot(self, plot):
+        # XXX for now, the only way that we have to customize properly
+        #     datetime labels on tooltips is to insert an additional column
+        #     cf. function onPlotHover in cubicweb.flot.js
+        if self.timemode:
+            plot = [(datetime2ticks(x), y, datetime2ticks(x)) for x,y in plot]
+        return dumps(plot)
+
+    def _render(self, req, width=500, height=400):
+        # XXX IE requires excanvas.js
+        req.add_js( ('jquery.flot.js', 'cubicweb.flot.js') )
+        figid = u'figure%s' % make_uid('foo')
+        plotdefs = []
+        plotdata = []
+        self.w(u'<div id="%s" style="width: %spx; height: %spx;"></div>' %
+               (figid, width, height))
+        for idx, (label, plot) in enumerate(zip(self.labels, self.plots)):
+            plotid = '%s_%s' % (figid, idx)
+            plotdefs.append('var %s = %s;' % (plotid, self.dump_plot(plot)))
+            plotdata.append("{label: '%s', data: %s}" % (label, plotid))
+        req.html_headers.add_onload(self.onload %
+                                    {'plotdefs': '\n'.join(plotdefs),
+                                     'figid': figid,
+                                     'plotdata': ','.join(plotdata),
+                                     'mode': self.timemode and "'time'" or 'null'})
+
+
+class PlotView(baseviews.AnyRsetView):
+    id = 'plot'
+    title = _('generic plot')
+    __select__ = at_least_two_columns() & all_columns_are_numbers()
+    timemode = False
+
+    def call(self, width=500, height=400):
+        # prepare data
+        rqlst = self.rset.syntax_tree()
+        # XXX try to make it work with unions
+        varnames = [var.name for var in rqlst.children[0].get_selected_variables()][1:]
+        abscissa = [row[0] for row in self.rset]
+        plots = []
+        nbcols = len(self.rset.rows[0])
+        for col in xrange(1, nbcols):
+            data = [row[col] for row in self.rset]
+            plots.append(filterout_nulls(abscissa, data))
+        plotwidget = FlotPlotWidget(varnames, plots, timemode=self.timemode)
+        plotwidget.render(self.req, width, height, w=self.w)
+
+
+class TimeSeriePlotView(PlotView):
+    __select__ = at_least_two_columns() & columns_are_date_then_numbers()
+    timemode = True
+
+
 try:
-    import matplotlib
-    import sys
-    if 'matplotlib.backends' not in sys.modules:
-        matplotlib.use('Agg')
-    from pylab import figure
+    from GChartWrapper import Pie, Pie3D
 except ImportError:
     pass
 else:
-    class PlotView(baseviews.AnyRsetView):
-        id = 'plot'
-        title = _('generic plot')
-        binary = True
-        content_type = 'image/png'
-        _plot_count = 0
-        __select__ = plot_selector()
+
+    class PieChartWidget(PlotWidget):
+        def __init__(self, labels, values, pieclass=Pie, title=None):
+            self.labels = labels
+            self.values = values
+            self.pieclass = pieclass
+            self.title = title
 
-        def call(self, width=None, height=None):
-            # compute dimensions
-            if width is None:
-                if 'width' in self.req.form:
-                    width = int(self.req.form['width'])
-                else:
-                    width = 500
+        def _render(self, width=None, height=None):
+            piechart = self.pieclass(self.values)
+            piechart.label(*self.labels)
+            if width is not None:
+                height = height or width
+                piechart.size(width, height)
+            if self.title:
+                piechart.title(self.title)
+            self.w(u'<img src="%s" />' % html_escape(piechart.url))
 
-            if height is None:
-                if 'height' in self.req.form:
-                    height = int(self.req.form['height'])
-                else:
-                    height = 400
-            dpi = 100.
+    class PieChartView(baseviews.AnyRsetView):
+        id = 'piechart'
+        pieclass = Pie
+
+        __select__ = at_least_two_columns() & second_column_is_number()
 
-            # compute data
-            abscisses = [row[0] for row in self.rset]
-            courbes = []
-            nbcols = len(self.rset.rows[0])
-            for col in xrange(1, nbcols):
-                courbe = [row[col] for row in self.rset]
-                courbes.append(courbe)
-            if not courbes:
-                raise Exception('no data')
-            # plot data
-            fig = figure(figsize=(width/dpi, height/dpi), dpi=dpi)
-            ax = fig.add_subplot(111)
-            colors = 'brgybrgy'
-            try:
-                float(abscisses[0])
-                xlabels = None
-            except ValueError:
-                xlabels = abscisses
-                abscisses = range(len(xlabels))
-            for idx, courbe in enumerate(courbes):
-                ax.plot(abscisses, courbe, '%sv-' % colors[idx], label=self.rset.description[0][idx+1])
-            ax.autoscale_view()
-            alldata = flatten(courbes)
-            m, M = min(alldata or [0]), max(alldata or [1])
-            if m is None: m = 0
-            if M is None: M = 0
-            margin = float(M-m)/10
-            ax.set_ylim(m-margin, M+margin)
-            ax.grid(True)
-            ax.legend(loc='best')
-            if xlabels is not None:
-                ax.set_xticks(abscisses)
-                ax.set_xticklabels(xlabels)
-            try:
-                fig.autofmt_xdate()
-            except AttributeError:
-                # XXX too old version of matplotlib. Ignore safely.
-                pass
+        def _guess_vid(self, row):
+            etype = self.rset.description[row][0]
+            if self.schema.eschema(etype).is_final():
+                return 'final'
+            return 'textincontext'
 
-            # save plot
-            filename = self.build_figname()
-            fig.savefig(filename, dpi=100)
-            img = open(filename, 'rb')
-            self.w(img.read())
-            img.close()
-            os.remove(filename)
+        def call(self, title=None, width=None, height=None):
+            labels = []
+            values = []
+            for rowidx, (_, value) in enumerate(self.rset):
+                if value is not None:
+                    vid = self._guess_vid(rowidx)
+                    label = '%s: %s' % (self.view(vid, self.rset, row=rowidx, col=0),
+                                        value)
+                    labels.append(label.encode(self.req.encoding))
+                    values.append(value)
+            pie = PieChartWidget(labels, values, pieclass=self.pieclass,
+                                 title=title)
+            if width is not None:
+                height = height or width
+            pie.render(width, height, w=self.w)
 
-        def build_figname(self):
-            self.__class__._plot_count += 1
-            return '/tmp/burndown_chart_%s_%d.png' % (self.config.appid, self.__class__._plot_count)
+
+    class PieChart3DView(PieChartView):
+        id = 'piechart3D'
+        pieclass = Pie3D
--- a/web/views/primary.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/primary.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """The default primary view
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 _ = unicode
@@ -141,7 +142,7 @@
         for box in boxes:
             if isinstance(box, tuple):
                 label, rset, vid  = box
-                self.w(u'<div class="sideRelated">')
+                self.w(u'<div class="sideBox">')
                 self.wview(vid, rset, title=label)
                 self.w(u'</div>')
             else:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/views/pyviews.py	Wed Jun 03 09:15:20 2009 +0200
@@ -0,0 +1,42 @@
+"""Views to display bare python values
+
+:organization: Logilab
+:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
+__docformat__ = "restructuredtext en"
+
+from cubicweb.view import View
+from cubicweb.selectors import match_kwargs
+
+class PyValTableView(View):
+    id = 'pyvaltable'
+    __select__ = match_kwargs('pyvalue')
+
+    def call(self, pyvalue, headers=None):
+        if headers is None:
+            headers = self.req.form.get('headers')
+        self.w(u'<table class="listing">\n')
+        if headers:
+            self.w(u'<tr>')
+            for header in headers:
+                self.w(u'<th>%s</th>' % header)
+            self.w(u'</tr>\n')
+        for row in pyvalue:
+            self.w(u'<tr>')
+            for cell in row:
+                self.w(u'<td>%s</td>' % cell)
+            self.w(u'</tr>\n')
+        self.w(u'</table>\n')
+
+
+class PyValListView(View):
+    id = 'pyvallist'
+    __select__ = match_kwargs('pyvalue')
+
+    def call(self, pyvalue):
+        self.w(u'<ul>\n')
+        for line in pyvalue:
+            self.w(u'<li>%s</li>\n' % line)
+        self.w(u'</ul>\n')
--- a/web/views/schema.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/schema.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Specific views for schema related entities
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -16,7 +17,8 @@
 from cubicweb.view import EntityView, StartupView
 from cubicweb.common import tags, uilib
 from cubicweb.web import action
-from cubicweb.web.views import TmpFileViewMixin, primary, baseviews
+from cubicweb.web.views import TmpFileViewMixin, primary, baseviews, tabs
+from cubicweb.web.facet import AttributeFacet
 
 
 class ViewSchemaAction(action.Action):
@@ -31,8 +33,6 @@
         return self.build_url(self.id)
 
 
-# schema entity types views ###################################################
-
 class CWRDEFPrimaryView(primary.PrimaryView):
     __select__ = implements('CWAttribute', 'CWRelation')
     cache_max_age = 60*60*2 # stay in http cache for 2 hours by default
@@ -42,6 +42,7 @@
                % (entity.dc_type().capitalize(),
                   html_escape(entity.dc_long_title())))
 
+# CWEType ######################################################################
 
 class CWETypeOneLineView(baseviews.OneLineView):
     __select__ = implements('CWEType')
@@ -55,32 +56,118 @@
         if final:
             self.w(u'</em>')
 
-
-# in memory schema views (yams class instances) ###############################
 SKIPPED_RELS = ('is', 'is_instance_of', 'identity', 'created_by', 'owned_by',
                 'has_text',)
 
-class CWETypeSchemaView(primary.PrimaryView):
-    id = 'eschema'
+class CWETypePrimaryView(tabs.TabsMixin, primary.PrimaryView):
     __select__ = implements('CWEType')
     title = _('in memory entity schema')
     main_related_section = False
     skip_rels = SKIPPED_RELS
+    tabs = [_('cwetype-schema-text'), _('cwetype-schema-image'), 
+            _('cwetype-schema-permissions'), _('cwetype-workflow')]
+    default_tab = 'cwetype-schema-text'
 
-    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(uilib.ureport_as_html(layout))
-        if not eschema.is_final():
-            msg = self.req._('graphical schema for %s') % entity.name
-            self.w(tags.img(src=entity.absolute_url(vid='eschemagraph'),
-                            alt=msg))
+    def render_entity(self, entity):
+        self.render_entity_title(entity)
+        self.w(u'<div>%s</div>' % entity.description)
+        self.render_tabs(self.tabs, self.default_tab, entity)
+
+
+class CWETypeSTextView(EntityView):
+    id = 'cwetype-schema-text'
+    __select__ = EntityView.__select__ & implements('CWEType')
+
+    def cell_call(self, row, col):
+        entity = self.entity(row, col)
+        self.w(u'<h2>%s</h2>' % _('Attributes'))
+        rset = self.req.execute('Any N,F,D,GROUP_CONCAT(C),I,J,DE,A '
+                                'GROUPBY N,F,D,AA,A,I,J,DE '
+                                'ORDERBY AA WHERE A is CWAttribute, '
+                                'A ordernum AA, A defaultval D, '
+                                'A constrained_by C?, A description DE, '
+                                'A fulltextindexed I, A internationalizable J, '
+                                'A relation_type R, R name N, '
+                                'A to_entity O, O name F, '
+                                'A from_entity S, S eid %(x)s',
+                                {'x': entity.eid})
+        self.wview('editable-table', rset, 'null', displayfilter=True)
+        self.w(u'<h2>%s</h2>' % _('Relations'))
+        rset = self.req.execute('Any N,C,F,M,K,D,A ORDERBY N '
+                                'WITH N,C,F,M,D,K,A BEING ('
+                                '(Any N,C,F,M,K,D,A '
+                                'ORDERBY N WHERE A is CWRelation, '
+                                'A description D, A composite K?, '
+                                'A relation_type R, R name N, '
+                                'A to_entity O, O name F, '
+                                'A cardinality C, O meta M, '
+                                'A from_entity S, S eid %(x)s)'
+                                ' UNION '
+                                '(Any N,C,F,M,K,D,A '
+                                'ORDERBY N WHERE A is CWRelation, '
+                                'A description D, A composite K?, '
+                                'A relation_type R, R name N, '
+                                'A from_entity S, S name F, '
+                                'A cardinality C, S meta M, '
+                                'A to_entity O, O eid %(x)s))'
+                                ,{'x': entity.eid})
+        self.wview('editable-table', rset, 'null', displayfilter=True)
 
 
+class CWETypeSImageView(EntityView):
+    id = 'cwetype-schema-image'
+    __select__ = EntityView.__select__ & implements('CWEType')
+
+    def cell_call(self, row, col):
+        entity = self.entity(row, col)
+        url = entity.absolute_url(vid='schemagraph')
+        self.w(u'<img src="%s" alt="%s"/>' % (
+            html_escape(url),
+            html_escape(self.req._('graphical schema for %s') % entity.name)))
+
+class CWETypeSPermView(EntityView):
+    id = 'cwetype-schema-permissions'
+    __select__ = EntityView.__select__ & implements('CWEType')
+
+    def cell_call(self, row, col):
+        entity = self.entity(row, col)
+        self.w(u'<h2>%s</h2>' % _('Add permissions'))
+        rset = self.req.execute('Any P WHERE X add_permission P, '
+                                'X eid %(x)s', 
+                                {'x': entity.eid})
+        self.wview('outofcontext', rset, 'null')
+        self.w(u'<h2>%s</h2>' % _('Read permissions'))
+        rset = self.req.execute('Any P WHERE X read_permission P, '
+                                'X eid %(x)s', 
+                                {'x': entity.eid})
+        self.wview('outofcontext', rset, 'null')
+        self.w(u'<h2>%s</h2>' % _('Update permissions'))
+        rset = self.req.execute('Any P WHERE X update_permission P, '
+                                'X eid %(x)s', 
+                                {'x': entity.eid})
+        self.wview('outofcontext', rset, 'null')
+        self.w(u'<h2>%s</h2>' % _('Delete permissions'))
+        rset = self.req.execute('Any P WHERE X delete_permission P, '
+                                'X eid %(x)s', 
+                                {'x': entity.eid})
+        self.wview('outofcontext', rset, 'null')
+
+class CWETypeSWorkflowView(EntityView):
+    id = 'cwetype-workflow'
+    __select__ = EntityView.__select__ & implements('CWEType')
+
+    def cell_call(self, row, col):
+        entity = self.entity(row, col)
+        if entity.reverse_state_of:
+            self.w(u'<img src="%s" alt="%s"/>' % (
+                    html_escape(entity.absolute_url(vid='ewfgraph')),
+                    html_escape(self.req._('graphical workflow for %s') % entity.name)))
+        else:
+            self.w(u'<p>%s</p>' % _('There is no workflow defined for this entity.'))
+
+# CWRType ######################################################################
+
 class CWRTypeSchemaView(primary.PrimaryView):
-    id = 'eschema'
     __select__ = implements('CWRType')
     title = _('in memory relation schema')
     main_related_section = False
@@ -93,25 +180,12 @@
         self.w(uilib.ureport_as_html(layout))
         if not rschema.is_final():
             msg = self.req._('graphical schema for %s') % entity.name
-            self.w(tags.img(src=entity.absolute_url(vid='eschemagraph'),
+            self.w(tags.img(src=entity.absolute_url(vid='schemagraph'),
                             alt=msg))
 
 
 # schema images ###############################################################
 
-class ImageView(EntityView):
-    __select__ = implements('CWEType')
-    id = 'image'
-    title = _('image')
-
-    def cell_call(self, row, col):
-        entity = self.entity(row, col)
-        url = entity.absolute_url(vid='eschemagraph')
-        self.w(u'<img src="%s" alt="%s"/>' % (
-            html_escape(url),
-            html_escape(self.req._('graphical schema for %s') % entity.name)))
-
-
 class RestrictedSchemaDotPropsHandler(s2d.SchemaDotPropsHandler):
     def __init__(self, req):
         # FIXME: colors are arbitrary
@@ -187,7 +261,7 @@
                        prophdlr=RestrictedSchemaDotPropsHandler(self.req))
 
 class CWETypeSchemaImageView(TmpFileViewMixin, EntityView):
-    id = 'eschemagraph'
+    id = 'schemagraph'
     __select__ = implements('CWEType')
 
     content_type = 'image/png'
@@ -211,3 +285,15 @@
         visitor = OneHopRSchemaVisitor(self.req, rschema)
         s2d.schema2dot(outputfile=tmpfile, visitor=visitor,
                        prophdlr=RestrictedSchemaDotPropsHandler(self.req))
+
+### facets
+
+class CWMetaFacet(AttributeFacet):
+    id = 'cwmeta-facet'
+    __select__ = AttributeFacet.__select__ & implements('CWEType')
+    rtype = 'meta'
+
+class CWFinalFacet(AttributeFacet):
+    id = 'cwfinal-facet'
+    __select__ = AttributeFacet.__select__ & implements('CWEType')
+    rtype = 'final'
--- a/web/views/searchrestriction.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/searchrestriction.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 a search
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/web/views/sessions.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/sessions.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 object :/
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -13,7 +14,7 @@
 
 class InMemoryRepositorySessionManager(AbstractSessionManager):
     """manage session data associated to a session identifier"""
-    
+
     def __init__(self):
         AbstractSessionManager.__init__(self)
         # XXX require a RepositoryAuthenticationManager which violates
@@ -23,7 +24,7 @@
 
     def current_sessions(self):
         return self._sessions.values()
-    
+
     def get_session(self, req, sessionid):
         """return existing session for the given session identifier"""
         if not sessionid in self._sessions:
@@ -47,13 +48,13 @@
 
     def open_session(self, req):
         """open and return a new session for the given request
-        
+
         :raise ExplicitLogin: if authentication is required
         """
         session = self.authmanager.authenticate(req)
         self._sessions[session.sessionid] = session
         return session
-    
+
     def close_session(self, session):
         """close session on logout or on invalid session detected (expired out,
         corrupted...)
@@ -66,4 +67,3 @@
             # already closed, may occurs if the repository session expired but
             # not the web session
             pass
-    
--- a/web/views/startup.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/startup.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 apply to a result set.
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 _ = unicode
@@ -12,12 +13,12 @@
 from logilab.mtconverter import html_escape
 
 from cubicweb.view import StartupView
-from cubicweb.selectors import match_user_groups
+from cubicweb.selectors import match_user_groups, implements
 from cubicweb.common.uilib import ureport_as_html
 from cubicweb.web import ajax_replace_url, uicfg, httpcache
+from cubicweb.web.views import tabs
 from cubicweb.web.views.management import SecurityViewMixIn
 
-
 class ManageView(StartupView):
     id = 'manage'
     title = _('manage')
@@ -163,42 +164,44 @@
     def display_folders(self):
         return 'Folder' in self.schema and self.req.execute('Any COUNT(X) WHERE X is Folder')[0][0]
 
-
-
-class SchemaView(StartupView):
+class SchemaView(tabs.TabsMixin, StartupView):
     id = 'schema'
     title = _('application schema')
+    tabs = [_('schema-text'), _('schema-image')]
+    default_tab = 'schema-text'
 
     def call(self):
         """display schema information"""
         self.req.add_js('cubicweb.ajax.js')
         self.req.add_css(('cubicweb.schema.css','cubicweb.acl.css'))
-        withmeta = int(self.req.form.get('withmeta', 0))
-        section = self.req.form.get('sec', '')
+        self.w(u'<h1>%s</h1>' % _('Schema of the data model'))
+        self.render_tabs(self.tabs, self.default_tab)
+
+class SchemaTabImageView(StartupView):
+    id = 'schema-image'
+
+    def call(self):
+        self.w(_(u'<div>This schema of the data model <em>excludes</em> the '
+                 u'meta-data, but you can also display a <a href="%s">complete '
+                 u'schema with meta-data</a>.</div>')
+               % html_escape(self.build_url('view', vid='schemagraph', withmeta=1)))
         self.w(u'<img src="%s" alt="%s"/>\n' % (
-            html_escape(self.req.build_url('view', vid='schemagraph', withmeta=withmeta)),
+            html_escape(self.req.build_url('view', vid='schemagraph', withmeta=0)),
             self.req._("graphical representation of the application'schema")))
-        if withmeta:
-            self.w(u'<div><a href="%s">%s</a></div>' % (
-                html_escape(self.build_url('schema', withmeta=0, sec=section)),
-                self.req._('hide meta-data')))
-        else:
-            self.w(u'<div><a href="%s">%s</a></div>' % (
-                html_escape(self.build_url('schema', withmeta=1, sec=section)),
-                self.req._('show meta-data')))
-        self.w(u'<a href="%s">%s</a><br/>' %
-               (html_escape(ajax_replace_url('detailed_schema', '', 'schematext',
-                                             skipmeta=int(not withmeta))),
-                self.req._('detailed schema view')))
-        if self.req.user.matching_groups('managers'):
-            self.w(u'<a href="%s">%s</a>' %
-                   (html_escape(ajax_replace_url('detailed_schema', '', 'schema_security',
-                                                 skipmeta=int(not withmeta))),
-                self.req._('security')))
-        self.w(u'<div id="detailed_schema">')
-        if section:
-            self.wview(section, None)
-        self.w(u'</div>')
+
+class SchemaTabTextView(StartupView):
+    id = 'schema-text'
+
+    def call(self):
+        self.w(u'<p>%s</p>' % _('This is the list of types defined in the data '
+                                'model ofin this application.'))
+        self.w(u'<p>%s</p>' % _('<em>meta</em> is True for types that are defined by the '
+                                'framework itself (e.g. User and Group). '
+                                '<em>final</em> is True for types that can not be the '
+                                'subject of a relation (e.g. Int and String).'))
+        rset = self.req.execute('Any X,M,F ORDERBY N WHERE X is CWEType, X name N, '
+                                'X meta M, X final F')
+        self.wview('editable-table', rset, displayfilter=True)
 
 
 class ManagerSchemaPermissionsView(StartupView, SecurityViewMixIn):
@@ -314,3 +317,4 @@
                                      skipmeta=skipmeta)
         self.w(ureport_as_html(layout))
 
+
--- a/web/views/tableview.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/tableview.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
--- a/web/views/tabs.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/tabs.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """base classes to handle tabbed views
 
 :organization: Logilab
-:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 __docformat__ = "restructuredtext en"
@@ -13,7 +14,7 @@
 from cubicweb.selectors import partial_has_related_entities
 from cubicweb.view import EntityView
 from cubicweb.common import tags, uilib
-
+from cubicweb.utils import make_uid
 
 class LazyViewMixin(object):
     """provides two convenience methods for the tab machinery
@@ -91,11 +92,11 @@
                 continue
         return selected_tabs
 
-    def render_tabs(self, tabs, default, entity):
+    def render_tabs(self, tabs, default, entity=None):
         # tabbed views do no support concatenation
         # hence we delegate to the default tab if there is more than on entity
         # in the result set
-        if len(self.rset) > 1:
+        if entity and len(self.rset) > 1:
             entity.view(default, w=self.w)
             return
         # XXX (syt) fix below add been introduced at some point to fix something
@@ -117,7 +118,11 @@
         active_tab = self.active_tab(tabs, default)
         # build the html structure
         w = self.w
-        w(u'<div id="entity-tabs-%s">' % entity.eid)
+        if entity:
+            w(u'<div id="entity-tabs-%s">' % entity.eid)
+        else:
+            uid = make_uid('tab')
+            w(u'<div id="entity-tabs-%s">' % uid)
         w(u'<ul>')
         for tab in tabs:
             w(u'<li>')
@@ -131,7 +136,10 @@
         w(u'</div>')
         for tab in tabs:
             w(u'<div id="as-%s">' % tab)
-            self.lazyview(tab, eid=entity.eid)
+            if entity:
+                self.lazyview(tab, eid=entity.eid)
+            else:
+                self.lazyview(tab, static=True)
             w(u'</div>')
         # call the set_tab() JS function *after* each tab is generated
         # because the callback binding needs to be done before
@@ -140,7 +148,7 @@
    set_tab('%(vid)s', '%(cookiename)s');
  """ % {'tabindex'   : tabs.index(active_tab),
         'vid'        : active_tab,
-        'eeid'       : entity.eid,
+        'eeid'       : (entity and entity.eid or uid),
         'cookiename' : self.cookie_name})
 
 
@@ -164,7 +172,7 @@
     """
     __select__ = EntityView.__select__ & partial_has_related_entities()
     vid = 'list'
-    
+
     def cell_call(self, row, col):
         rset = self.entity(row, col).related(self.rtype, role(self))
         self.w(u'<div class="mainInfo">')
--- a/web/views/timeline.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/timeline.py	Wed Jun 03 09:15:20 2009 +0200
@@ -3,8 +3,9 @@
 cf. http://code.google.com/p/simile-widgets/
 
 :organization: Logilab
-:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -16,6 +17,7 @@
 from cubicweb.selectors import implements
 from cubicweb.view import EntityView, StartupView
 
+_ = unicode
 
 class TimelineJsonView(EntityView):
     """generates a json file to feed Timeline.loadJSON()
@@ -67,7 +69,7 @@
             return None
         event_data = {'start': start.strftime(self.date_fmt),
                       'title': html_escape(entity.dc_title()),
-                      'description': entity.dc_description(),
+                      'description': entity.dc_description(format='text/html'),
                       'link': entity.absolute_url(),
                       }
         onclick = self.onclick(entity)
@@ -101,6 +103,7 @@
 class TimelineView(TimelineViewMixIn, EntityView):
     """builds a cubicweb timeline widget node"""
     id = 'timeline'
+    title = _('timeline')
     __select__ = implements(ICalendarable)
     need_navigation = False
     def call(self, tlunit=None):
--- a/web/views/timetable.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/timetable.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """html calendar views
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 from logilab.mtconverter import html_escape
--- a/web/views/treeview.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/treeview.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,22 +1,23 @@
 """Set of tree-building widgets, based on jQuery treeview plugin
 
 :organization: Logilab
-:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
+from logilab.common.decorators import monkeypatch
 from logilab.mtconverter import html_escape
 
+from cubicweb.utils import make_uid
 from cubicweb.interfaces import ITree
 from cubicweb.selectors import implements
 from cubicweb.view import EntityView
-from cubicweb.utils import make_uid
 
 def treecookiename(treeid):
     return str('treestate-%s' % treeid)
 
-
 class TreeView(EntityView):
     id = 'treeview'
     itemvid = 'treeitemview'
@@ -25,27 +26,23 @@
 
     def call(self, subvid=None, treeid=None, initial_load=True):
         if subvid is None:
-            if 'subvid' in self.req.form:
-                subvid = self.req.form.pop('subvid') # consume it
-            else:
-                subvid = 'oneline'
+            subvid = self.req.form.pop('subvid', 'oneline') # consume it
         if treeid is None:
-            if 'treeid' in self.req.form:
-                treeid = self.req.form.pop('treeid')
-            else:
+            treeid = self.req.form.pop('treeid', None)
+            if treeid is None:
+                self.warning('Tree state won\'t be properly restored after next reload')
                 treeid = make_uid('throw away uid')
-                self.warning('Tree state won\'t be properly restored after next reload')
-        if initial_load:
+        self.w(u'<ul id="tree-%s" class="%s">' % (treeid, self.css_classes))
+        for rowidx in xrange(len(self.rset)):
+            self.wview(self.itemvid, self.rset, row=rowidx, col=0,
+                       vid=subvid, parentvid=self.id, treeid=treeid)
+        self.w(u'</ul>')
+        if initial_load and not self.req.form.get('fname'):
             self.req.add_css('jquery.treeview.css')
             self.req.add_js(('cubicweb.ajax.js', 'jquery.treeview.js'))
             self.req.html_headers.add_onload(u"""
 jQuery("#tree-%s").treeview({toggle: toggleTree, prerendered: true});""" % treeid)
-        self.w(u'<ul id="tree-%s" class="%s">' % (treeid, self.css_classes))
-        for rowidx in xrange(len(self.rset)):
-            self.wview(self.itemvid, self.rset, row=rowidx, col=0,
-                       vid=subvid, parentvid=self.id)
-        self.w(u'</ul>')
-        
+
 
 class FileTreeView(TreeView):
     """specific version of the treeview to display file trees
@@ -57,8 +54,6 @@
     def call(self, subvid=None, treeid=None, initial_load=True):
         super(FileTreeView, self).call(treeid=treeid, subvid='filetree-oneline', initial_load=initial_load)
 
-
-
 class FileItemInnerView(EntityView):
     """inner view used by the TreeItemView instead of oneline view
 
@@ -70,17 +65,18 @@
     def cell_call(self, row, col):
         entity = self.entity(row, col)
         if ITree.is_implemented_by(entity.__class__) and not entity.is_leaf():
-            self.w(u'<div class="folder">%s</div>' % entity.view('oneline'))
+            self.w(u'<div class="folder">%s</div>\n' % entity.view('oneline'))
         else:
             # XXX define specific CSS classes according to mime types
-            self.w(u'<div class="file">%s</div>' % entity.view('oneline'))
+            self.w(u'<div class="file">%s</div>\n' % entity.view('oneline'))
 
 
 class DefaultTreeViewItemView(EntityView):
     """default treeitem view for entities which don't implement ITree"""
     id = 'treeitemview'
-    
-    def cell_call(self, row, col, vid='oneline', parentvid='treeview'):
+
+    def cell_call(self, row, col, vid='oneline', parentvid='treeview', treeid=None):
+        assert treeid is not None
         entity = self.entity(row, col)
         itemview = self.view(vid, self.rset, row=row, col=col)
         if row == len(self.rset) - 1:
@@ -95,33 +91,61 @@
     (each item should be expandable if it's not a tree leaf)
     """
     id = 'treeitemview'
-    __select__ = implements(ITree)
-    
-    def cell_call(self, row, col, vid='oneline', parentvid='treeview'):
+    __select__ = EntityView.__select__ & implements(ITree) # XXX
+
+    def open_state(self, eeid, treeid):
+        cookies = self.req.get_cookie()
+        treestate = cookies.get(treecookiename(treeid))
+        if treestate:
+            return str(eeid) in treestate.value.split(';')
+        return False
+
+    def cell_call(self, row, col, treeid, vid='oneline', parentvid='treeview'):
+        w = self.w
         entity = self.entity(row, col)
-        cssclasses = []
-        is_leaf = False
-        if row == len(self.rset) - 1:
-            is_leaf = True
+        liclasses = []
+        is_last = row == len(self.rset) - 1
+        is_open = self.open_state(entity.eid, treeid)
         if not hasattr(entity, 'is_leaf') or entity.is_leaf():
-            if is_leaf : cssclasses.append('last')
-            self.w(u'<li class="%s">' % u' '.join(cssclasses))
+            if is_last:
+                liclasses.append('last')
+            w(u'<li class="%s">' % u' '.join(liclasses))
         else:
             rql = entity.children_rql() % {'x': entity.eid}
             url = html_escape(self.build_url('json', rql=rql, vid=parentvid,
                                              pageid=self.req.pageid,
-                                             subvid=vid,
-                                             noautoload=True))
-            cssclasses.append('expandable')
-            divclasses = ['hitarea expandable-hitarea']
-            if is_leaf :
-                cssclasses.append('lastExpandable')
-                divclasses.append('lastExpandable-hitarea')
-            self.w(u'<li cubicweb:loadurl="%s" class="%s">' % (url, u' '.join(cssclasses)))
-            self.w(u'<div class="%s"> </div>' % u' '.join(divclasses))
+                                             treeid=treeid,
+                                             fname='view',
+                                             subvid=vid))
+            divclasses = ['hitarea']
+            if is_open:
+                liclasses.append('collapsable')
+                divclasses.append('collapsable-hitarea')
+            else:
+                liclasses.append('expandable')
+                divclasses.append('expandable-hitarea')
+            if is_last:
+                if is_open:
+                    liclasses.append('lastCollapsable')
+                    divclasses.append('lastCollapsable-hitarea')
+                else:
+                    liclasses.append('lastExpandable')
+                    divclasses.append('lastExpandable-hitarea')
+            if is_open:
+                w(u'<li class="%s">' % u' '.join(liclasses))
+            else:
+                w(u'<li cubicweb:loadurl="%s" class="%s">' % (url, u' '.join(liclasses)))
+            divtail = """ onclick="asyncRemoteExec('node_clicked', '%s', '%s')" """ %\
+                (treeid, entity.eid)
+            w(u'<div class="%s"%s></div>' % (u' '.join(divclasses), divtail))
+
             # add empty <ul> because jquery's treeview plugin checks for
             # sublists presence
-            self.w(u'<ul class="placeholder"><li>place holder</li></ul>')
+            if not is_open:
+                w(u'<ul class="placeholder"><li>place holder</li></ul>')
+        # the local node info
         self.wview(vid, self.rset, row=row, col=col)
-        self.w(u'</li>')
+        if is_open: # => not leaf => rql is defined
+            self.wview(parentvid, self.req.execute(rql), treeid=treeid, initial_load=False)
+        w(u'</li>')
 
--- a/web/views/urlpublishing.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/urlpublishing.py	Wed Jun 03 09:15:20 2009 +0200
@@ -18,8 +18,9 @@
 because of redirecting instead of direct traversal
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 __docformat__ = "restructuredtext en"
@@ -35,7 +36,7 @@
     """exception used by url evaluators to notify they can't evaluate
     a path
     """
-    
+
 class URLPublisherComponent(Component):
     """associate url's path to view identifier / rql queries,
     by applying a chain of urlpathevaluator components.
@@ -50,33 +51,33 @@
     something else than `PathDontMatch` will stop the handlers chain.
     """
     id = 'urlpublisher'
-    
+
     def __init__(self, default_method='view'):
         super(URLPublisherComponent, self).__init__()
         self.default_method = default_method
-        evaluators = []        
+        evaluators = []
         for evaluatorcls in self.vreg.registry_objects('components',
                                                        'urlpathevaluator'):
             # instantiation needed
             evaluator = evaluatorcls(self)
             evaluators.append(evaluator)
         self.evaluators = sorted(evaluators, key=lambda x: x.priority)
-        
+
     def process(self, req, path):
         """given an url (essentialy caracterized by a path on the server,
         but additional information may be found in the request object), return
         a publishing method identifier (eg controller) and an optional result
         set
-        
+
         :type req: `cubicweb.web.Request`
         :param req: the request object
-        
+
         :type path: str
         :param path: the path of the resource to publish
 
         :rtype: tuple(str, `cubicweb.common.utils.ResultSet` or None)
         :return: the publishing method identifier and an optional result set
-        
+
         :raise NotFound: if no handler is able to decode the given path
         """
         parts = [part for part in path.split('/')
@@ -97,7 +98,7 @@
             pmid = self.default_method
         return pmid, rset
 
-        
+
 class URLPathEvaluator(Component):
     __abstract__ = True
     id = 'urlpathevaluator'
@@ -136,7 +137,7 @@
             raise NotFound()
         return None, rset
 
-        
+
 class RestPathEvaluator(URLPathEvaluator):
     """handle path with the form::
 
@@ -149,7 +150,7 @@
         for etype in self.schema.entities():
             etype = str(etype)
             self.etype_map[etype.lower()] = etype
-            
+
     def evaluate_path(self, req, parts):
         if not (0 < len(parts) < 4):
             raise PathDontMatch()
@@ -177,7 +178,7 @@
 
     def cls_rset(self, req, cls):
         return req.execute(cls.fetch_rql(req.user))
-        
+
     def attr_rset(self, req, etype, attrname, value):
         rql = u'Any X WHERE X is %s, X %s %%(x)s' % (etype, attrname)
         if attrname == 'eid':
@@ -211,7 +212,7 @@
             except KeyError:
                 continue
         raise PathDontMatch()
-        
+
 
 class ActionPathEvaluator(URLPathEvaluator):
     """handle path with the form::
--- a/web/views/urlrewrite.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/urlrewrite.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """Rules based url rewriter component, to get configurable RESTful urls
 
 :organization: Logilab
-:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 import re
 
@@ -73,8 +74,8 @@
     rules = [
         ('/schema',  dict(vid='schema')),
         ('/index', dict(vid='index')),
-        ('/myprefs', dict(vid='epropertiesform')),
-        ('/siteconfig', dict(vid='systemepropertiesform')),
+        ('/myprefs', dict(vid='propertiesform')),
+        ('/siteconfig', dict(vid='systempropertiesform')),
         ('/manage', dict(vid='manage')),
         ('/notfound', dict(vid='404')),
         ('/error', dict(vid='error')),
--- a/web/views/vcard.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/vcard.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,15 +1,16 @@
 """vcard import / export
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
 from cubicweb.selectors import implements
 from cubicweb.view import EntityView
 
-_ = unicode 
+_ = unicode
 
 VCARD_PHONE_TYPES = {'home': 'HOME', 'office': 'WORK', 'mobile': 'CELL', 'fax': 'FAX'}
 
@@ -19,12 +20,12 @@
     title = _('vcard')
     templatable = False
     content_type = 'text/x-vcard'
-    __select__ = implements('CWUser')        
+    __select__ = implements('CWUser')
 
     def set_request_content_type(self):
         """overriden to set a .vcf filename"""
         self.req.set_content_type(self.content_type, filename='vcard.vcf')
-        
+
     def cell_call(self, row, col):
         self.vcard_header()
         self.vcard_content(self.complete_entity(row, col))
@@ -33,11 +34,11 @@
     def vcard_header(self):
         self.w(u'BEGIN:vcard\n')
         self.w(u'VERSION:3.0\n')
-        
+
     def vcard_footer(self):
         self.w(u'NOTE:this card has been generated by CubicWeb\n')
         self.w(u'END:vcard\n')
-        
+
     def vcard_content(self, entity):
         who = u'%s %s' % (entity.surname or '',
                           entity.firstname or '')
--- a/web/views/wdoc.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/wdoc.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """inline help system, using ReST file in products `wdoc` directory
 
 :organization: Logilab
-:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -53,7 +54,7 @@
         node = index[section.attrib['appendto']]
         idx = None
     return node, idx
-                     
+
 def build_toc(config):
     alltocfiles = reversed(tuple(config.locate_all_files('toc.xml')))
     maintoc = parse(alltocfiles.next()).getroot()
@@ -61,7 +62,7 @@
     index = {}
     build_toc_index(maintoc, index)
     # insert component documentation into the tree according to their toc.xml
-    # file 
+    # file
     for fpath in alltocfiles:
         toc = parse(fpath).getroot()
         for section in toc:
@@ -73,7 +74,7 @@
             section.parent = node
             build_toc_index(section, index)
     return index
-    
+
 def title_for_lang(node, lang):
     for title in node.findall('title'):
         if title.attrib['{http://www.w3.org/XML/1998/namespace}lang'] == lang:
@@ -88,7 +89,7 @@
     __select__ = match_form_params('fid')
     id = 'wdoc'
     title = _('site documentation')
-    
+
     def call(self):
         fid = self.req.form['fid']
         for lang in chain((self.req.lang, self.vreg.property_value('ui.language')),
@@ -107,7 +108,7 @@
         else:
             self.navigation_links(node)
             self.w(u'<div class="hr"></div>')
-            self.w(u'<h1>%s</h1>' % (title_for_lang(node, self.req.lang)))            
+            self.w(u'<h1>%s</h1>' % (title_for_lang(node, self.req.lang)))
         data = open(join(resourcedir, rid)).read()
         self.w(rest_publish(self, data))
         if node is not None:
@@ -124,17 +125,17 @@
         self.w(u'<div class="docnav">\n')
         previousidx = brothers.index(node) - 1
         if previousidx >= 0:
-            self.navsection(brothers[previousidx], 'prev')            
-        self.navsection(parent, 'up')            
+            self.navsection(brothers[previousidx], 'prev')
+        self.navsection(parent, 'up')
         nextidx = brothers.index(node) + 1
         if nextidx < len(brothers):
-            self.navsection(brothers[nextidx], 'next')            
+            self.navsection(brothers[nextidx], 'next')
         self.w(u'</div>\n')
 
     navinfo = {'prev': ('', 'data/previous.png', _('i18nprevnext_previous')),
                'next': ('', 'data/next.png', _('i18nprevnext_next')),
                'up': ('', 'data/up.png', _('i18nprevnext_up'))}
-               
+
     def navsection(self, node, navtype):
         htmlclass, imgpath, msgid = self.navinfo[navtype]
         self.w(u'<span class="%s">' % htmlclass)
@@ -143,7 +144,7 @@
             self.req.build_url('doc/'+node.attrib['resource']),
             title_for_lang(node, self.req.lang)))
         self.w(u'</span>\n')
-        
+
     def subsections_links(self, node, first=True):
         sub = subsections(node)
         if not sub:
@@ -158,7 +159,7 @@
             self.subsections_links(child, False)
             self.w(u'</li>')
         self.w(u'</ul>\n')
-        
+
 
 
 class InlineHelpImageView(StartupView):
@@ -167,7 +168,7 @@
     binary = True
     templatable = False
     content_type = 'image/png'
-    
+
     def call(self):
         fid = self.req.form['fid']
         for lang in chain((self.req.lang, self.vreg.property_value('ui.language')),
@@ -185,7 +186,7 @@
     id = 'changelog'
     title = _('What\'s new?')
     maxentries = 25
-    
+
     def call(self):
         rid = 'ChangeLog_%s' % (self.req.lang)
         allentries = []
@@ -234,4 +235,3 @@
                     break
         w('') # blank line
         self.w(rest_publish(self, '\n'.join(restdata)))
-        
--- a/web/views/workflow.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/workflow.py	Wed Jun 03 09:15:20 2009 +0200
@@ -4,8 +4,9 @@
 * workflow entities views (State, Transition, TrInfo)
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 _ = unicode
@@ -21,21 +22,22 @@
 from cubicweb.web.form import FormViewMixIn
 from cubicweb.web.formfields import StringField,  RichTextField
 from cubicweb.web.formwidgets import HiddenInput, SubmitButton, Button
-from cubicweb.web.views import TmpFileViewMixin
-from cubicweb.web.views.boxes import EditBox
+from cubicweb.web.views import TmpFileViewMixin, forms
 
 
 # IWorkflowable views #########################################################
 
-class ChangeStateForm(form.EntityFieldsForm):
+class ChangeStateForm(forms.EntityFieldsForm):
     id = 'changestate'
 
+    form_renderer_id = 'base' # don't want EntityFormRenderer
+    form_buttons = [SubmitButton(stdmsgs.YES),
+                     Button(stdmsgs.NO, cwaction='cancel')]
+
     __method = StringField(name='__method', initial='set_state',
                            widget=HiddenInput)
     state = StringField(eidparam=True, widget=HiddenInput)
     trcomment = RichTextField(label=_('comment:'), eidparam=True)
-    form_buttons = [SubmitButton(stdmsgs.YES),
-                     Button(stdmsgs.NO, cwaction='cancel')]
 
 
 class ChangeStateFormView(FormViewMixIn, view.EntityView):
--- a/web/views/xbel.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/xbel.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """xbel views
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 _ = unicode
--- a/web/views/xmlrss.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/views/xmlrss.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,10 +1,12 @@
 """base xml and rss views
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
+_ = unicode
 
 from time import timezone
 
@@ -17,8 +19,6 @@
 from cubicweb.web.box import BoxTemplate
 from cubicweb.common.uilib import simple_sgml_tag
 
-_ = unicode
-
 
 # base xml views ##############################################################
 
--- a/web/webconfig.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/webconfig.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """common web configuration for twisted/modpython applications
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -22,7 +23,7 @@
     ('site-title',
      {'type' : 'string', 'default': 'unset title',
       'help': _('site title'),
-      'sitewide': True, 'group': 'ui', 
+      'sitewide': True, 'group': 'ui',
       }),
     ('main-template',
      {'type' : 'string', 'default': 'main-template',
@@ -54,7 +55,7 @@
       'help': _('maximum number of entities to display in related combo box'),
       'group': 'navigation',
       }),
-    
+
     ))
 
 
@@ -64,7 +65,7 @@
     """
     cubicweb_vobject_path = CubicWebConfiguration.cubicweb_vobject_path | set(['web/views'])
     cube_vobject_path = CubicWebConfiguration.cube_vobject_path | set(['views'])
-    
+
     options = merge_options(CubicWebConfiguration.options + (
         ('anonymous-user',
          {'type' : 'string',
@@ -185,7 +186,7 @@
           'interface\'s language according to browser defined preferences',
           'group': 'web', 'inputlevel': 2,
           }),
-        
+
         ('print-traceback',
          {'type' : 'yn',
           'default': not CubicWebConfiguration.mode == 'installed',
@@ -206,17 +207,17 @@
 
     def fckeditor_installed(self):
         return exists(self.ext_resources['FCKEDITOR_PATH'])
-    
+
     def eproperty_definitions(self):
         for key, pdef in super(WebConfiguration, self).eproperty_definitions():
             if key == 'ui.fckeditor' and not self.fckeditor_installed():
                 continue
             yield key, pdef
-                
+
     # method used to connect to the repository: 'inmemory' / 'pyro'
     # Pyro repository by default
     repo_method = 'pyro'
-    
+
     # don't use @cached: we want to be able to disable it while this must still
     # be cached
     def repository(self, vreg=None):
@@ -235,7 +236,7 @@
 
     def vc_config(self):
         return self.repository().get_versions()
-    
+
     # mapping to external resources (id -> path) (`external_resources` file) ##
     ext_resources = {
         'FAVICON':  'DATADIR/favicon.ico',
@@ -244,13 +245,13 @@
         'HELP':     'DATADIR/help.png',
         'CALENDAR_ICON': 'DATADIR/calendar.gif',
         'SEARCH_GO':'DATADIR/go.png',
-        
+
         'FCKEDITOR_PATH':  '/usr/share/fckeditor/',
-        
+
         'IE_STYLESHEETS':    ['DATADIR/cubicweb.ie.css'],
         'STYLESHEETS':       ['DATADIR/cubicweb.css'],
         'STYLESHEETS_PRINT': ['DATADIR/cubicweb.print.css'],
-        
+
         'JAVASCRIPTS':       ['DATADIR/jquery.js',
                               'DATADIR/jquery.corner.js',
                               'DATADIR/jquery.json.js',
@@ -258,8 +259,8 @@
                               'DATADIR/cubicweb.python.js',
                               'DATADIR/cubicweb.htmlhelpers.js'],
         }
-        
-        
+
+
     def anonymous_user(self):
         """return a login and password to use for anonymous users. None
         may be returned for both if anonymous connections are not allowed
@@ -272,7 +273,7 @@
         if user is not None:
             user = unicode(user)
         return user, passwd
-    
+
     def has_resource(self, rid):
         """return true if an external resource is defined"""
         return bool(self.ext_resources.get(rid))
@@ -281,19 +282,19 @@
     def locate_resource(self, rid):
         """return the directory where the given resource may be found"""
         return self._fs_locate(rid, 'data')
-            
+
     @cached
     def locate_doc_file(self, fname):
         """return the directory where the given resource may be found"""
         return self._fs_locate(fname, 'wdoc')
-            
+
     def _fs_locate(self, rid, rdirectory):
         """return the directory where the given resource may be found"""
         path = [self.apphome] + self.cubes_path() + [join(self.shared_dir())]
         for directory in path:
             if exists(join(directory, rdirectory, rid)):
                 return join(directory, rdirectory)
-            
+
     def locate_all_files(self, rid, rdirectory='wdoc'):
         """return all files corresponding to the given resource"""
         path = [self.apphome] + self.cubes_path() + [join(self.shared_dir())]
@@ -308,7 +309,7 @@
         # load external resources definition
         self._build_ext_resources()
         self._init_base_url()
-        
+
     def _init_base_url(self):
         # normalize base url(s)
         baseurl = self['base-url']
@@ -341,11 +342,11 @@
 
 
     # static files handling ###################################################
-    
+
     @property
     def static_directory(self):
         return join(self.appdatahome, 'static')
-    
+
     def static_file_exists(self, rpath):
         return exists(join(self.static_directory, rpath))
 
@@ -365,4 +366,3 @@
     def static_file_del(self, rpath):
         if self.static_file_exists(rpath):
             os.remove(join(self.static_directory, rpath))
-        
--- a/web/webctl.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/webctl.py	Wed Jun 03 09:15:20 2009 +0200
@@ -2,8 +2,9 @@
 web configuration
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -12,7 +13,7 @@
 
 class WebCreateHandler(CommandHandler):
     cmdname = 'create'
-    
+
     def bootstrap(self, cubes, inputlevel=0):
         """bootstrap this configuration"""
         print '** generic web configuration'
@@ -23,9 +24,8 @@
             print '-' * 72
             config.input_config('pyro-client', inputlevel)
         if confirm('allow anonymous access', False):
-            config.global_set_option('anonymous-user', 'anon') 
-            config.global_set_option('anonymous-password', 'anon') 
-        
+            config.global_set_option('anonymous-user', 'anon')
+            config.global_set_option('anonymous-password', 'anon')
+
     def postcreate(self):
         """hooks called once application's initialization has been completed"""
-        
--- a/web/widgets.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/web/widgets.py	Wed Jun 03 09:15:20 2009 +0200
@@ -4,8 +4,9 @@
 serialization time
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -152,7 +153,7 @@
         if example:
             help.append(u'<span>(%s: %s)</span>'
                         % (req._('sample format'), example))
-	help.append(u'</div>')
+        help.append(u'</div>')
         return u'&nbsp;'.join(help)
 
     def render_example(self, req):
--- a/wsgi/__init__.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/wsgi/__init__.py	Wed Jun 03 09:15:20 2009 +0200
@@ -7,8 +7,9 @@
 WSGI corresponding PEP: http://www.python.org/dev/peps/pep-0333/
 
 :organization: Logilab
-:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
 
@@ -25,7 +26,7 @@
         return _pformat(obj)
     except:
         return u'<could not parse>'
-    
+
 def qs2dict(qs):
     """transforms a query string into a regular python dict"""
     result = {}
@@ -35,7 +36,7 @@
 
 def normalize_header(header):
     """returns a normalized header name
-    
+
     >>> normalize_header('User_Agent')
     'User-agent'
     """
@@ -84,4 +85,3 @@
         else:
             post.setdefault(key, []).append(submessage.get_payload())
     return post, files
-
--- a/wsgi/handler.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/wsgi/handler.py	Wed Jun 03 09:15:20 2009 +0200
@@ -1,8 +1,9 @@
 """WSGI request handler for cubicweb
 
 :organization: Logilab
-:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 __docformat__ = "restructuredtext en"
@@ -74,7 +75,7 @@
 
     def __iter__(self):
         return iter(self.body)
-    
+
 
 
 class CubicWebWSGIApplication(object):
@@ -100,7 +101,7 @@
             self.url_rewriter = self.appli.vreg.select_component('urlrewriter')
         except ObjectNotFound:
             self.url_rewriter = None
-        
+
     def _render(self, req):
         """this function performs the actual rendering
         XXX missing: https handling, url rewriting, cache management,
@@ -156,8 +157,8 @@
             # 500 Internal server error
             return self.redirect(req, req.build_url('error'))
         return WSGIResponse(200, req, result)
-        
-    
+
+
     def __call__(self, environ, start_response):
         """WSGI protocol entry point"""
         req = CubicWebWsgiRequest(environ, self.appli.vreg, self.base_url)
@@ -170,7 +171,7 @@
         self.debug('redirecting to %s', location)
         req.set_header('location', str(location))
         return WSGIResponse(303, req)
-        
+
     def request_auth(self, req, loggedout=False):
         """returns the appropriate WSGIResponse to require the user to log in
         """
--- a/wsgi/request.py	Wed Jun 03 09:09:33 2009 +0200
+++ b/wsgi/request.py	Wed Jun 03 09:15:20 2009 +0200
@@ -5,8 +5,9 @@
   http://www.djangoproject.com/
 
 :organization: Logilab
-:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
 __docformat__ = "restructuredtext en"
@@ -25,7 +26,7 @@
 class CubicWebWsgiRequest(CubicWebRequestBase):
     """most of this code COMES FROM DJANO
     """
-    
+
     def __init__(self, environ, vreg, base_url=None):
         self.environ = environ
         self.path = environ['PATH_INFO']
@@ -42,7 +43,7 @@
             self.form.update(files)
         # prepare output headers
         self.headers_out = {}
-        
+
     def __repr__(self):
         # Since this is called as part of error handling, we need to be very
         # robust against potentially malformed input.
@@ -52,14 +53,14 @@
             (form, meta)
 
     ## cubicweb request interface ################################################
-    
+
     def base_url(self):
         return self._base_url
 
     def http_method(self):
         """returns 'POST', 'GET', 'HEAD', etc."""
         return self.method
-    
+
     def relative_path(self, includeparams=True):
         """return the normalized path of the request (ie at least relative
         to the application's root, but some other normalization may be needed
@@ -74,7 +75,7 @@
             qs = self.environ.get('QUERY_STRING')
             if qs:
                 return '%s?%s' % (path, qs)
-        
+
         return path
 
     def get_header(self, header, default=None):
@@ -82,7 +83,7 @@
         raise KeyError if the header is not set
         """
         return self._headers.get(normalize_header(header), default)
-    
+
     def set_header(self, header, value, raw=True):
         """set an output HTTP header"""
         assert raw, "don't know anything about non-raw headers for wsgi requests"
@@ -91,7 +92,7 @@
     def add_header(self, header, value):
         """add an output HTTP header"""
         self.headers_out[header] = value
-    
+
     def remove_header(self, header):
         """remove an output HTTP header"""
         self.headers_out.pop(header, None)
@@ -101,9 +102,9 @@
         mx date time value (GMT), else return None
         """
         return None
-        
+
     ## wsgi request helpers ###################################################
-    
+
     def application_uri(self):
         """Return the application's base URI (no PATH_INFO or QUERY_STRING)
 
@@ -123,7 +124,7 @@
                     url += ':' + environ['SERVER_PORT']
         url += quote(environ.get('SCRIPT_NAME') or '/')
         return url
-        
+
     def get_full_path(self):
         return '%s%s' % (self.path, self.environ.get('QUERY_STRING', '') and ('?' + self.environ.get('QUERY_STRING', '')) or '')